How to Dismiss a Storyboard Popover

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 in shouldPerformSegueWithIdentifier:, and
  • set ourself as the popover controller's delegate in order to catch popoverControllerDidDismissPopover: and also set myPopover = 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.

    • Your storyboard should look like this if you add the navigation controller in storyboard.

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 the UIPopoverPresentationController.
    Once again we will do this in prepareForSegue:

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



Leave a reply



Submit