如何closures故事板popup窗口
我使用Xcode Storyboards创build了一个UIBarButtonItem
(所以没有代码),像这样:
呈现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提供的源代码