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
How to Dismiss a Storyboard Popover
提问by Sam Spencer
I've created a popover from a UIBarButtonItem
using Xcode Storyboards (so there's no code) like this:
我已经从UIBarButtonItem
使用 Xcode Storyboards(所以没有代码)创建了一个弹出窗口,如下所示:
Presenting the popover works just fine. However, I can't get the popover to disappearwhen I tap the UIBarButtonItem
that 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 UIBarButtonItem
for 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 确保再次点击弹出框会隐藏弹出框而不是显示重复。它也适用于UIPresentationController
Xcode 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 nil
again, so we're back to our initial state.
这里使用归零弱引用的好处是,一旦弹出控制器被解除——无论是通过编程方式shouldPerformSegueWithIdentifier:
,还是通过用户点击弹出窗口之外的其他地方自动——ivarnil
再次运行,所以我们回到我们的初始状态。
Without zeroing weak references, we'd have to also:
如果不将弱引用归零,我们还必须:
- set
myPopover = nil
when dismissing it inshouldPerformSegueWithIdentifier:
, and - set ourself as the popover controller's delegate in order to catch
popoverControllerDidDismissPopover:
and also setmyPopover = nil
there (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 ixPopoverBarButtonItem
that 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 的类定义为我的自定义类:
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