ios 如何关闭故事板弹出框

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8287242/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 15:36:50  来源:igfitidea点击:

How to Dismiss a Storyboard Popover

iosobjective-cuipopovercontrolleruibarbuttonitemuistoryboard

提问by Sam Spencer

I've created a popover from a UIBarButtonItemusing Xcode Storyboards (so there's no code) like this:

我已经从UIBarButtonItem使用 Xcode Storyboards(所以没有代码)创建了一个弹出窗口,如下所示:

Xcode 5.0 Connections Inspector with Popover

带有 Popover 的 Xcode 5.0 连接检查器

Presenting the popover works just fine. However, I can't get the popover to disappearwhen I tap the UIBarButtonItemthat made it appear.

呈现弹出框效果很好。但是,当我点击使其出现的时,我无法让弹出窗口消失UIBarButtonItem

When the button is pressed (first time) the popover appears. When the button is pressed again (second time) the same popover appears on top of it, so now I have two popovers (or more if I continuer pressing the button). According to the iOS Human Interface GuidelinesI need to make the popover appear on the first tap and disappear on the second:

当按钮被按下(第一次)时,弹出窗口出现。当再次按下按钮(第二次)时,相同的弹出窗口会出现在它的顶部,所以现在我有两个弹出窗口(如果我继续按下按钮,则会有更多)。根据iOS 人机界面指南,我需要使弹出框出现在第一次点击并在第二次点击时消失:

Ensure that only one popover is visible onscreen at a time. You should not display more than one popover (or custom view designed to look and behave like a popover) at the same time. In particular, you should avoid displaying a cascade or hierarchy of popovers simultaneously, in which one popover emerges from another.

确保屏幕上一次只能看到一个弹出窗口。您不应同时显示多个弹出窗口(或设计为看起来和行为类似于弹出窗口的自定义视图)。特别是,您应该避免同时显示层叠或层次结构的弹出窗口,其中一个弹出窗口从另一个弹出窗口出现。

How can I dismiss the popover when the user taps the UIBarButtonItemfor a second time?

当用户UIBarButtonItem第二次点击时如何关闭弹出窗口?

回答by rickster

EDIT:These problems appear to be fixed as of iOS 7.1 / Xcode 5.1.1. (Possibly earlier, as I haven't been able to test all versions. Definitely after iOS 7.0, since I tested that one.) When you create a popover segue from a UIBarButtonItem, the segue makes sure that tapping the popover again hides the popover rather than showing a duplicate. It works right for the new UIPresentationController-based popover segues that Xcode 6 creates for iOS 8, too.

编辑:这些问题似乎已从 iOS 7.1 / Xcode 5.1.1 开始修复。(可能更早,因为我无法测试所有版本。肯定是在 iOS 7.0 之后,因为我测试了那个版本。)当您从 a 创建弹出框 segue 时UIBarButtonItem,segue 确保再次点击弹出框会隐藏弹出框而不是显示重复。它也适用于UIPresentationControllerXcode 6 为 iOS 8 创建的基于新的 popover segues。

Since my solution may be of historical interest to those still supporting earlier iOS versions, I've left it below.

由于我的解决方案可能对那些仍然支持早期 iOS 版本的人具有历史意义,因此我将其留在下面。



If you store a reference to the segue's popover controller, dismissing it before setting it to a new value on repeat invocations of prepareForSegue:sender:, all you avoid is the problem of getting multiple stacking popovers on repeated presses of the button -- you still can't use the button to dismiss the popover as the HIG recommends (and as seen in Apple's apps, etc.)

如果您存储对 segue 的 popover 控制器的引用,在重复调用 时将其设置为新值之前将其解除,则prepareForSegue:sender:您所避免的只是重复按下按钮时出现多个堆叠弹出框的问题——您仍然无法使用按照 HIG 的建议关闭弹出窗口的按钮(以及在 Apple 的应用程序等中看到的)

You can take advantage of ARC zeroing weak references for a simple solution, though:

不过,您可以利用 ARC 将弱引用归零来获得一个简单的解决方案:

1: Segue from the button

1:从按钮开始

As of iOS 5, you couldn't make this work with a segue from a UIBarButtonItem, but you can on iOS 6 and later. (On iOS 5, you'd have to segue from the view controller itself, then have the button's action call performSegueWithIdentifier:after checking for the popover.)

从 iOS 5 开始,您无法使用来自 的 segue 进行此操作UIBarButtonItem,但在 iOS 6 及更高版本上可以。(在 iOS 5 上,您必须从视图控制器本身进行切换,然后performSegueWithIdentifier:在检查弹出框后调用按钮的操作。)

2: Use a reference to the popover in -shouldPerformSegue...

2:使用对弹出窗口的引用 -shouldPerformSegue...

@interface ViewController
@property (weak) UIPopoverController *myPopover;
@end

@implementation ViewController
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    // if you have multiple segues, check segue.identifier
    self.myPopover = [(UIStoryboardPopoverSegue *)segue popoverController];
}
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender {
    if (self.myPopover) {
        [self.myPopover dismissPopoverAnimated:YES];
        return NO;
    } else {
        return YES;
    }
}
@end

3: There's no step three!

3:没有第三步!

The nice thing about using a zeroing weak reference here is that once the popover controller is dismissed -- whether programmatically in shouldPerformSegueWithIdentifier:, or automatically by the user tapping somewhere else outside the popover -- the ivar goes to nilagain, so we're back to our initial state.

这里使用归零弱引用的好处是,一旦弹出控制器被解除——无论是通过编程方式shouldPerformSegueWithIdentifier:,还是通过用户点击弹出窗口之外的其他地方自动——ivarnil再次运行,所以我们回到我们的初始状态。

Without zeroing weak references, we'd have to also:

如果不将弱引用归零,我们还必须:

  • set myPopover = nilwhen dismissing it in shouldPerformSegueWithIdentifier:, and
  • set ourself as the popover controller's delegate in order to catch popoverControllerDidDismissPopover:and also set myPopover = nilthere (so we catch when the popover is automatically dismissed).
  • myPopover = nil在 中关闭它时设置shouldPerformSegueWithIdentifier:,并且
  • 将我们自己设置为 popover 控制器的委托,以便捕获popoverControllerDidDismissPopover:并设置myPopover = nil在那里(因此我们在自动关闭 popover 时捕获)。

回答by jorgecarreira

I found the solution here https://stackoverflow.com/a/7938513/665396In first prepareForSegue:sender: store in a ivar/property the pointer to the UIPopoverController and user that pointer to dismiss the popover in the subsequent invocations.

我在这里找到了解决方案https://stackoverflow.com/a/7938513/665396首先 prepareForSegue:sender: 将指向 UIPopoverController 的指针和用户的指针存储在 ivar/property 中,以在后续调用中关闭弹出框。

...
@property (nonatomic, weak) UIPopoverController* storePopover;
...

- (void)prepareForSegue:(UIStoryboardSegue *)segue 
                 sender:(id)sender {
if ([segue.identifier isEqualToString:@"My segue"]) {
// setup segue here

[self.storePopover dismissPopoverAnimated:YES];
self.storePopover = ((UIStoryboardPopoverSegue*)segue).popoverController;
...
}

回答by Peter Blazejewicz

I've used custom segue for this.

我为此使用了自定义转场。

1

1

create custom segue to use in Storyboard:

创建要在 Storyboard 中使用的自定义 segue:

@implementation CustomPopoverSegue
-(void)perform
{
    // "onwer" of popover - it needs to use "strong" reference to retain UIPopoverReference
    ToolbarSearchViewController *source = self.sourceViewController;
    UIViewController *destination = self.destinationViewController;
    // create UIPopoverController
    UIPopoverController *popoverController = [[UIPopoverController alloc] initWithContentViewController:destination];
    // source is delegate and owner of popover
    popoverController.delegate = source;
    popoverController.passthroughViews = [NSArray arrayWithObject:source.searchBar];
    source.recentSearchesPopoverController = popoverController;
    // present popover
    [popoverController presentPopoverFromRect:source.searchBar.bounds 
                                       inView:source.searchBar
                     permittedArrowDirections:UIPopoverArrowDirectionAny
                                     animated:YES];

}
@end

2

2

in view controller that is source/input of segue e.g. start segue with action:

在作为segue的源/输入的视图控制器中,例如用动作开始segue:

-(void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar
{
    if(nil == self.recentSearchesPopoverController)
    {
        NSString *identifier = NSStringFromClass([CustomPopoverSegue class]);
        [self performSegueWithIdentifier:identifier sender:self];
    } 
}

3

3

references are assigned by segue which creates UIPopoverController - when dismissing popover

引用由创建 UIPopoverController 的 segue 分配 - 解除弹出框时

-(void)searchBarTextDidEndEditing:(UISearchBar *)searchBar
{
    if(self.recentSearchesPopoverController)
    {
        [self.recentSearchesPopoverController dismissPopoverAnimated:YES];
        self.recentSearchesPopoverController = nil;
    }    
}

regards, Peter

问候, 彼得

回答by Papick G. Taboada

I solved it creating a custom ixPopoverBarButtonItemthat either triggers the segue or dismisses the popover being shown.

我解决了它创建一个自定义ixPopoverBarButtonItem触发 segue 或关闭正在显示的弹出窗口。

What I do: I toggle the action & target of the button, so it either triggers the segue, or disposes the currently showing popover.

我做什么:我切换按钮的动作和目标,因此它要么触发 segue,要么处理当前显示的弹出框。

It took me a lot of googling for this solution, I don't want to take the credits for the idea of toggling the action. Putting the code into a custom button was my approach to keep the boilerplate code in my view to a minimum.

我花了很多谷歌搜索这个解决方案,我不想因为切换动作的想法而受到赞扬。将代码放入自定义按钮是我将样板代码保持在最低限度的方法。

In the storyboard, I define the class of the BarButtonItem to my custom class:

在故事板中,我将 BarButtonItem 的类定义为我的自定义类:

custom bar button

自定义栏按钮

Then I pass the popover created by the segue to my custom button implementation in the prepareForSegue:sender:method:

然后我将 segue 创建的 popover 传递给方法中的自定义按钮实现prepareForSegue:sender:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender  
{
    if ([segue.identifier isEqualToString:@"myPopoverSegue"]) {
        UIStoryboardPopoverSegue* popSegue = (UIStoryboardPopoverSegue*)segue;
        [(ixPopoverBarButtonItem *)sender showingPopover:popSegue.popoverController];
    }
}

Btw... since I have more than one buttons triggering popovers, I still have to keep a reference of the currently displayed popover and dismiss it when I make the new one visible, but this was not your question...

顺便说一句...因为我有多个按钮触发弹出框,所以我仍然必须保留当前显示的弹出框的参考,并在我使新弹出框可见时将其关闭,但这不是你的问题......

Here is how I implemented my custom UIBarButtonItem:

这是我如何实现我的自定义 UIBarButtonItem:

...interface:

...界面:

@interface ixPopoverBarButtonItem : UIBarButtonItem

- (void) showingPopover:  (UIPopoverController *)popoverController;

@end

... and impl:

...并实现:

#import "ixPopoverBarButtonItem.h"
@interface ixPopoverBarButtonItem  ()
@property (strong, nonatomic) UIPopoverController *popoverController;
@property (nonatomic)         SEL                  tempAction;           
@property (nonatomic,assign)  id                   tempTarget; 

- (void) dismissPopover;

@end

@implementation ixPopoverBarButtonItem

@synthesize popoverController = _popoverController;
@synthesize tempAction = _tempAction;
@synthesize tempTarget = _tempTarget;

-(void)showingPopover:(UIPopoverController *)popoverController {

    self.popoverController = popoverController;
    self.tempAction = self.action;
    self.tempTarget = self.target;
    self.action = @selector(dismissPopover);
    self.target = self;
}    

-(void)dismissPopover {
    [self.popoverController dismissPopoverAnimated:YES];
    self.action = self.tempAction;
    self.target = self.tempTarget;

    self.popoverController = nil;
    self.tempAction = nil;
    self.tempTarget = nil;
}


@end

ps: I am new to ARC, so I am not entirely sure if I am leaking here. Please tell me if I am...

ps:我是ARC的新手,所以我不完全确定我是否在这里泄漏。请告诉我,如果我...

回答by Q8i

I have solved this problem with no need to keep a copy of a UIPopoverController. Simply handle everything in storyboard (Toolbar, BarButtons. etc.), and

我已经解决了这个问题,无需保留UIPopoverController. 只需处理故事板中的所有内容(工具栏、BarButtons 等),并且

  • handle visibility of the popover by a boolean,
  • make sure there is a delegate, and it is set to self
  • 通过布尔值处理弹出窗口的可见性,
  • 确保有一个委托,并将其设置为 self

Here is all the code:

这是所有的代码:

ViewController.h

视图控制器.h

@interface ViewController : UIViewController <UIPopoverControllerDelegate>
@end

ViewController.m

视图控制器.m

@interface ViewController ()
@property BOOL isPopoverVisible;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.isPopoverVisible = NO;
}

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    // add validations here... 
    self.isPopoverVisible = YES;
    [[(UIStoryboardPopoverSegue*)segue popoverController] setDelegate:self];
}

- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender {
    return !self.isPopoverVisible;
}

- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController {
    self.isPopoverVisible = NO;
}
@end

回答by Tod Cunningham

I took rickster's answer and packaged it into a class derived from UIViewController. This solution does require the following:

我接受了 rickster 的回答,并将其打包成一个派生自 UIViewController 的类。此解决方案确实需要以下内容:

  • iOS 6 (or later) with ARC
  • Derive your view controller from this class
  • make sure to call the "super" versions of prepareForSegue:sender and shouldPerformSegueWithIdentifier:sender if you are overriding those methods
  • Use a named popover segue
  • 带有 ARC 的 iOS 6(或更高版本)
  • 从这个类派生你的视图控制器
  • 如果要覆盖这些方法,请确保调用 prepareForSegue:sender 和 shouldPerformSegueWithIdentifier:sender 的“超级”版本
  • 使用命名的 popover segue

The nice thing about this is you don't have to do any "special" coding to support the proper handling of Popovers.

这样做的好处是您不必执行任何“特殊”编码来支持正确处理 Popover。

Interface:

界面

@interface FLStoryboardViewController : UIViewController
{
    __strong NSString            *m_segueIdentifier;
    __weak   UIPopoverController *m_popoverController;
}

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender;
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender;
@end

Implementation:

实施

@implementation FLStoryboardViewController

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if( [segue isKindOfClass:[UIStoryboardPopoverSegue class]] )
    {
        UIStoryboardPopoverSegue *popoverSegue = (id)segue;

        if( m_popoverController  ==  nil )
        {
            assert( popoverSegue.identifier.length >  0 );    // The Popover segue should be named for this to work fully
            m_segueIdentifier   = popoverSegue.identifier;
            m_popoverController = popoverSegue.popoverController;
        }
        else
        {
            [m_popoverController dismissPopoverAnimated:YES];
            m_segueIdentifier = nil;
            m_popoverController = nil;
        }
    }
    else
    {
        [super prepareForSegue:segue sender:sender];
    }
}


- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender
{
    // If this is an unnamed segue go ahead and allow it
    if( identifier.length != 0 )
    {
        if( [identifier compare:m_segueIdentifier]  ==  NSOrderedSame )
        {
            if( m_popoverController == NULL )
            {
                m_segueIdentifier = nil;
                return YES;
            }
            else
            {
                [m_popoverController dismissPopoverAnimated:YES];
                m_segueIdentifier = nil;
                m_popoverController = nil;
                return NO;
            }
        }
    }

    return [super shouldPerformSegueWithIdentifier:identifier sender:sender];
}

@end

Source available on GitHub

可在 GitHub 上获得源代码