如何closures故事板popup窗口

我使用Xcode Storyboards创build了一个UIBarButtonItem (所以没有代码),像这样:

Xcode 5.0连接督察与弹出

呈现popover工作得很好。 但是,当我点击出现的UIBarButtonItem时,我无法让UIBarButtonItem 消失

当按下(第一次)button时,popup窗口出现。 当再次按下button时(第二次),顶部会出现相同的popup窗口,所以现在我有两个popup窗口(或更多,如果我继续按下button)。 根据iOS人机界面指南,我需要让popover出现在第一个水龙头上,然后消失在第二个水龙头上:

确保一次只能在屏幕上看到一个popup窗口。 您不应该同时显示多个popup窗口(或devise为看起来和行为像popup窗口的自定义视图)。 特别是,你应该避免同时显示一个级联或层级的popovers,其中popover从另一个出现。

当用户UIBarButtonItem点击UIBarButtonItem时,我怎样才能解除UIBarButtonItem

编辑:这些问题似乎是固定的iOS 7.1 / Xcode 5.1.1。 (可能更早,因为我还没有能够testing所有版本,肯定是在iOS 7.0之后,因为我testing过了。)当从UIBarButtonItem创build一个UIBarButtonItem ,segue确保再次点击UIBarButtonItem隐藏popover而不是显示重复。 它适用于Xcode 6为iOS 8创build的新的基于UIPresentationController的popover segues。

由于我的解决scheme可能对那些仍然支持早期iOS版本的人有历史意义,所以我把它留在下面。


如果您存储对segue的popover控制器的引用,请在重复调用prepareForSegue:sender:之前将其设置为新值,然后避免在重复按下button时获得多个堆叠popup的问题 – 您仍然不能像HIG推荐的那样使用该button来解除popup窗口(以及苹果的应用程序等)

尽pipe如此,您可以利用ARC归零弱引用来获得简单的解决scheme:

1:从button来的

从iOS 5开始,您无法使用UIBarButtonItem的segue工作,但可以在iOS 6及更高版本中使用。 (在iOS 5上,您必须从视图控制器本身继续,然后让button的操作调用performSegueWithIdentifier:在检查popup窗口之后)。

2:在-shouldPerformSegue...使用对-shouldPerformSegue...的引用-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:没有第三步!

在这里使用一个调零弱引用的shouldPerformSegueWithIdentifier:是,一旦popover控制器被解散 – 无论是编程方式在shouldPerformSegueWithIdentifier: ,还是由用户自动地在shouldPerformSegueWithIdentifier:外的其他地方点击 – 这个ivar会再次nil ,所以我们回到我们的初始状态。

如果没有把弱引用归零,我们还必须:

  • 设置myPopover = nil时解雇它在shouldPerformSegueWithIdentifier:
  • 设置自己作为popover控制器的委托,以捕获popoverControllerDidDismissPopover:并且还设置myPopover = nil那里(所以我们赶上时,自动解散myPopover = nil )。

我在这里find了解决schemehttps://stackoverflow.com/a/7938513/665396在第一个prepareForSegue:发件人:存储在一个ivar /属性的指针UIPopoverController和用户的指针,解散在随后的调用popover。

 ... @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; ... } 

我用这个自定义的segue。

1

在Storyboard中创build自定义的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

在视图控制器是源的input/input,例如开始继续和动作:

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

3

引用由segue分配,创buildUIPopoverController – 解散popover时

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

问候,彼得

我解决了它创build一个自定义的ixPopoverBarButtonItem触发segue或ixPopoverBarButtonItem显示的popup窗口。

我所做的:我切换button的动作和目标,因此它会触发segue,或者处理当前显示的popup窗口。

这个解决scheme花了我大量的search,我不想为切换行动的想法拿信用。 将代码放入自定义button是我的方法,将我的视图中的样板代码降到最低。

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

自定义栏按钮

然后,我将由segue创build的popover传递给prepareForSegue:sender:方法中的自定义button实现:

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

顺便说一句,因为我有不止一个button触发popup,我仍然必须保持当前显示的popup式的参考,并消除它,当我让新的可见,但这不是你的问题…

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

…接口:

 @interface ixPopoverBarButtonItem : UIBarButtonItem - (void) showingPopover: (UIPopoverController *)popoverController; @end 

…和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:我是ARC新手,所以我不完全确定是否在这里泄漏。 请告诉我,如果我…

我已经解决了这个问题,不需要保留一个UIPopoverController的副本。 简单地处理故事板中的所有内容(工具栏,BarButton等)和

  • 通过布尔值来处理popover的可见性,
  • 确保有一个委托,它被设置为自我

这里是所有的代码:

ViewController.h

 @interface ViewController : UIViewController <UIPopoverControllerDelegate> @end 

ViewController.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 

我采取了rickster的答案,并将其打包成一个从UIViewController派生的类。 该解决scheme确实需要以下内容:

  • iOS 6(或更高版本)与ARC
  • 从这个类派生你的视图控制器
  • 请确保调用prepareForSegue:sender的“超级”版本并且shouldPerformSegueWithIdentifier:发送方,如果您重写了这些方法
  • 使用命名的popover segue

关于这一点的好处是你不必做任何“特殊”编码来支持正确处理Popovers。

界面

 @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 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 

GitHub提供的源代码