如何驳回一个故事板爆米花

我使用 Xcode 情节串连板(因此没有代码)从 UIBarButtonItem创建了一个弹出窗口,如下所示:

Xcode 5.0 Connections Inspector with Popover

展示酥饼就可以了。然而,当我点击使其显示的 UIBarButtonItem时,我无法将泡芙放到 消失上。

当按钮被按下时(第一次)弹出窗口出现。当按钮再次按下(第二次)同样的弹出窗口出现在它的顶部,所以现在我有两个弹出窗口(或更多,如果我继续按下按钮)。根据 IOS 人机界面指南,我需要让泡芙出现在第一个水龙头和消失在第二个:

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

当用户第二次点击 UIBarButtonItem时,我怎样才能消除弹出窗口?

46864 次浏览

我在这里找到了解决 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;
...
}

我用过自定义接口。

1

创建故事板中使用的自定义接口:

@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

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

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

3

引用是通过 segue 分配的,它创建 UIPopoverController-当忽略 popover 时

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

问候, 彼得

我解决了它创建了一个自定义 ixPopoverBarButtonItem,要么触发转折点,要么解除弹出窗口正在显示。

我所做的: 我切换按钮的动作和目标,所以它要么触发转换,要么处理当前显示的弹出窗口。

我花了很多时间在谷歌上搜索这个解决方案,我不想因为切换动作的想法而获得荣誉。将代码放入一个自定义按钮是我将样板代码在视图中保持在最低限度的方法。

在情节串连图板中,我将 BarButtonItem 的类定义为我的自定义类:

custom bar button

然后,我将 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];
}
}

顺便说一下,因为我有多个按钮触发泡芙,我仍然必须保持一个参考目前显示的泡芙,并忽略它当我使新的一个可见,但这不是你的问题..。

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

接口:

@interface ixPopoverBarButtonItem : UIBarButtonItem


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


@end

暗示:

#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

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

编辑: 这些问题似乎在 iOS 7.1/Xcode 5.1时已经解决了。(可能更早,因为我还没有能够测试所有的版本。绝对是在 iOS 7.0之后,因为我测试过那个。)当您从 UIBarButtonItem创建一个 popover segue 时,segue 确保再次点击 popover 将隐藏 popover,而不是显示一个副本。它适用于 Xcode 6为 iOS8创建的新的基于 UIPresentationController的 popover 接口。

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


如果你存储了一个对 segue 的 popover 控制器的引用,在重复调用 prepareForSegue:sender:时将其设置为一个新值之前将其忽略,你所能避免的就是在重复按下按钮时会出现多次叠加 popover 的问题——你仍然不能像 HIG 建议的那样使用该按钮来忽略 popover (正如在苹果的应用程序中看到的那样)

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

第1集: 继续从按钮

从 iOS5开始,你不能在 UIBarButtonItem的基础上做到这一点,但是你可以在 iOS6和更高版本上做到。(在 iOS5上,你必须从视图控制器本身开始,然后在检查弹出窗口之后让按钮的操作调用 performSegueWithIdentifier:。)

2: 使用 -shouldPerformSegue...中的 popover 引用

@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

没有第三步!

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

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

  • 设置 myPopover = nil时,它在 shouldPerformSegueWithIdentifier:中消失,和
  • 将我们自己设置为 popover 控制器的委托,以捕捉 popoverControllerDidDismissPopover:并在那里设置 myPopover = nil(这样当 popover 被自动解除时我们捕捉)。

我将 rickster 的答案打包成一个从 UIViewController 派生的类:

  • 具有 ARC 的 iOS6(或更高版本)
  • 从此类派生视图控制器
  • 如果要重写这些方法,请确保调用 prepareForSegue 的“ super”版本: sender 和 should dPerformSegueWithIdentifier: sender
  • 使用一个命名的酥饼接口

这样做的好处是你不需要做任何“特殊”的编码来支持正确的 Popover 处理。

界面 :

@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

我已经解决了这个问题,没有必要保留一份 UIPopoverController的副本。简单地处理故事板中的所有事情(工具栏,BarButtons。等) ,以及

  • 通过一个布尔值来处理弹出窗口的可见性,
  • 确保有一个委托,并将其设置为 self

所有代码如下:

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