How to Dismiss a Storyboard Popover
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.
Since my solution may be of historical interest to those still supporting earlier iOS versions, I've left it below.
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.)
You can take advantage of ARC zeroing weak references for a simple solution, though:
1: Segue from the button
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.)
2: Use a reference to the popover in -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!
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.
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).
How to use dismiss an iPhone popover in a Storyboard
It's not that different from objective-c. You could probably figure out what's going on just by looking at it. It's just setting up prepare for segue and implementing part of a protocol.
myViewController needs to declare itself as conforming to UIAdaptivePresentationControllerDelegate. Then this is the code.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:@"myPopoverSegueName"]) {
UIViewController *viewController = segue.destinationViewController;
viewController.popoverPresentationController.delegate = self
}
}
- (UIViewController *)presentationController:(UIPresentationController *)controller
viewControllerForAdaptivePresentationStyle:(UIModalPresentationStyle)style
{
UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(dismiss)];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:controller.presentedViewController];
navController.topViewController.navigationItem.leftBarButtonItem = doneButton;
}
-(void) dismiss
{
[self dismissViewControllerAnimated:YES completion:nil];
}
You don't need to use an extension or category. Not going to vouch for whether this does what Travis M. says it does.
How to dismiss first popover and show second popver in Swift 5?
Just reference the presentingViewController
, and when you dismiss the first popover, open the other after it has been dismissed.
@IBAction func btnManualEntry(_ sender: Any) {
if let presentingVC = self.presentingViewController {
self.dismiss(animated: true) {
if let vc = self.storyboard?.instantiateViewController(withIdentifier:"SummaryManualEditVC") {
vc.modalTransitionStyle = .crossDissolve;
vc.modalPresentationStyle = .overCurrentContext
presentingVC.present(vc, animated: true, completion: nil)
}
}
}
}
Trying to dismiss a popover that was presented by storyboard
You have to replace self
with nil
(for the object
parameter) when creating the notification observer since it is not self
that posts the notification:
[[NSNotificationCenter defaultCenter] addObserverForName:@"closePopover" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
[self dismissViewControllerAnimated:YES completion:nil];
}];
How to use dismiss an iPhone popover in an Adaptive Storyboard
You could add the navigation controller like this-
- Set your popover view controller as the root view controller to a navigation controller.
- Delete the popover segue that you are currently using
- Reconnect the segue from the button you are displaying the popover from to the navigation controller.
On iPad you will get a popover and on iPhone you will get a modal presentation. Both the iPad and iPhone will show the navigation controller. Depending on your use case this may or may not be something you want. Here's a screen show of what the storyboard should look like.
If you really do want your view controller to always be a popover leave your storyboard the way it is and add something like this to your view controller that presents the popover-
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:@"Your segue name"]) {
UIViewController *yourViewController = segue.destinationViewController;
yourViewController.modalPresentationStyle = UIModalPresentationPopover;
UIPopoverPresentationController *popoverPresentationController = yourViewController.popoverPresentationController;
popoverPresentationController.delegate = self;
}
}
The view controller presenting the popover will also need to respond to this UIPopoverPresentationDelegate
method
- (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller
{
return UIModalPresentationNone;//always popover.
}
Lastly, you could do the following to only add the navigation controller to the modal presentation of your view controller on the iPhone and leave the popover on iPad without a navigation controller.
- Leave your storyboard as is.
The proper place to inject the navigation controller is in
- (UIViewController *)presentationController:(UIPresentationController *)controller viewControllerForAdaptivePresentationStyle:(UIModalPresentationStyle)style
. In order for this to be called we must set ourselves as the delegate of theUIPopoverPresentationController
.
Once again we will do this inprepareForSegue:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:@"Your segue name"]) {
UIViewController *yourViewController = segue.destinationViewController;
yourViewController.modalPresentationStyle = UIModalPresentationPopover;
UIPopoverPresentationController *popoverPresentationController = yourViewController.popoverPresentationController;
popoverPresentationController.delegate = self;
}
}
Then we will do this in the delegate method that I mentioned above
-(UIViewController *)presentationController:(UIPresentationController *)controller viewControllerForAdaptivePresentationStyle:(UIModalPresentationStyle)style
{
UIViewController *presentedViewController = controller.presentedViewController;
UINavigationController *navigationController = [[UINavigationController alloc]
initWithRootViewController:presentedViewController];
UIBarButtonItem *dismissButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonItemStyleDone target:self action:@selector(done:)];
presentedViewController.navigationItem.rightBarButtonItem = dismissButton;
return navigationController;
}
Good Luck!
How to dismiss a popover from parent in swift
Just call dismiss method using parent's presentedViewController property, like ....
self.presentedViewController.dismissViewControllerAnimated(true, completion: nil)
For Swift 3.0
self.presentedViewController?.dismiss(animated: true, completion: nil)
dismiss popover in ios on didSelectRowAtIndexPath
If you meant that a tableView is inside that popover and your popover controller is instantiated like this:
Objective-C
In the containing controller, place this on the top of it:
@property (nonatomic,strong) UIPopoverController *popOver;
//this is the content of the popover
MyTableVC *tableVC = [self.storyboard instantiateViewControllerWithIdentifier:@"myTableView"];
//this is the navigation controller of your tableViewController
UINavigationController *popNav = [[UINavigationController alloc] initWithRootViewController:tableVC];
//this is you popover
self.popOver =[[UIPopoverController alloc] initWithContentViewController:popNav];
then you have to dismiss it inside that viewController, which you have created the popover from, in this case it's popNav for example.
So in you MyTableVC class you need to call this method in the didSelectRowAtIndexPath method:
[self dismissViewControllerAnimated:YES completion:nil];
Related Topics
Uibutton in Cell in Collection View Not Receiving Touch Up Inside Event
Facebook Login - iOS 9 - Without Safari
How to Get Available Wifi Network Name in iOS Using Swift
How to Use Pull to Refresh in Swift
iOS Different Font Sizes Within Single Size Class for Different Devices
Dynamic Height for Static Table Cells with Wrapping Labels
iOS Autolayout: Two Buttons of Equal Width, Side by Side
Uikeyboardframebeginuserinfokey & Uikeyboardframeenduserinfokey
Uitextview Link Detection in iOS 7
How to Check If My Avplayer Is Buffering
Showing the File Download Progress with Nsurlsessiondatatask
Use Table View Disclosure Indicator Style for Uibutton iOS
How to Use Navigation Controller Inside of Uitabbarcontroller with Storyboard on Swift
Autolayout Problems with iOS8 with Code That Works Fine on iOS7