繁体   English   中英

如何解雇故事板Popover

[英]How to Dismiss a Storyboard Popover

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

带有Popover的Xcode 5.0 Connections Inspector

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

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

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

当用户第二次点击UIBarButtonItem时,如何解除UIBarButtonItem

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

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


如果您存储对segue的弹出控制器的引用,在重新调用prepareForSegue:sender:之前将其设置为新值之前将其解除,您要避免的是在重复按下按钮时获得多个堆栈弹出窗口的问题 - 您仍然不能像HIG推荐的那样使用按钮来关闭弹出框(如Apple的应用程序中所见)

但是,您可以利用ARC归零弱引用来获得简单的解决方案:

1:从按钮开始

从iOS 5开始,你无法使用来自UIBarButtonItem的segue,但你可以在iOS 6及更高版本上使用。 (在iOS 5上,您必须从视图控制器本身中删除,然后按钮的动作调用performSegueWithIdentifier:检查弹出窗口后。)

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:是,一旦弹出控制器被解散 - 无论是以编程方式在shouldPerformSegueWithIdentifier: ,还是由用户在弹出窗口外的其他位置自动shouldPerformSegueWithIdentifier: - ivar再次变为nil ,所以我们是回到我们的初始状态。

在没有归零弱引用的情况下,我们还必须:

  • shouldPerformSegueWithIdentifier:解除它时设置myPopover = nil shouldPerformSegueWithIdentifier:
  • popoverControllerDidDismissPopover:设置为myPopover = nil控制器的委托,以便捕获popoverControllerDidDismissPopover:并在那里设置myPopover = nil (所以我们在弹出窗口被自动关闭时捕获)。

我在这里找到了解决方案https://stackoverflow.com/a/7938513/665396在第一个prepareForSegue:sender:存储在ivar / property中指向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;
...
}

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

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

我花了很多谷歌搜索这个解决方案,我不想拿转换为切换动作的想法。 将代码放入自定义按钮是我将样板代码保持在我的视图中的最小方法。

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

自定义栏按钮

然后我将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

...并且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的副本。 只需处理故事板中的所有内容(工具栏,BarButtons等),以及

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

这是所有代码:

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

我已经使用了自定义segue。

1

创建要在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

视图控制器是segue的源/输入,例如启动segue with action:

-(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;
    }    
}

亲爱的,彼得

我把rickster的答案打包并打包成一个派生自UIViewController的类。 此解决方案需要以下内容:

  • 带有ARC的iOS 6(或更高版本)
  • 从此类派生您的视图控制器
  • 如果要覆盖这些方法,请务必调用prepareForSegue:sender和shouldPerformSegueWithIdentifier:sender的“超级”版本
  • 使用命名的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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM