Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use dismiss an iPhone popover in an Adaptive Storyboard

I am new to iOS development, and am trying to learn storyboarding, Swift, and the new features of iOS 8 at the same time.

I have created a very simple storyboard that uses a Popover presentation segue to display another view. On the simulator, if I run this for an iPad, it works as expected. However, if I run it for an iPhone, instead of a popover, it displays a full-screen view, on top of the original view. This is fine; however, there is no way to dismiss it and go back to the original screen.

I have watched the WWDC 2014 video "228 A Look inside presentation controllers" and they can show a dismiss button if they build the user interface entirely with code.

I have also watched the "411 What's new in interface builder" session, where they say that this can be done in Interface Builder, but they do not show it, promising to show how to do it in the lab, if anyone is interested. Unfortunately, I did not attend WWDC 2014, or know anyone who has. My Google searches have not returned anything helpful either.

like image 584
Eduardo Avatar asked Sep 16 '14 04:09

Eduardo


3 Answers

In my case, I had a small popup that I wanted to be a popup on both an iPhone and iPad - and wanted to avoid using a navigation bar with a Dismiss. Discovered that one needed to implement two delegate calls (Swift 3.0):

extension MyViewController : UIPopoverPresentationControllerDelegate {
    // Needed for iPhone popup
    func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
        return .none
    }

    // Needed for iPhone in landscape
    func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
        return .none
    }
}
like image 198
anorskdev Avatar answered Sep 22 '22 21:09

anorskdev


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!

like image 12
Nat Osten Avatar answered Nov 19 '22 21:11

Nat Osten


If what you want is a popover on your iPad but a modal sheet with a close button on your iPhone then you can do it without creating an extra navigation controller in storyboard for the popover.

In Xcode 6.3 storyboard, you simply hook up a view controller and designate the segue as a "Present as Popover"

The code below should go in the view controller that segues to the popover, not in the popover itself:

First you set up the popover delegate:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if (segue.identifier == "myPopoverSegueName") {
        let vc = segue.destinationViewController
        vc.popoverPresentationController?.delegate = self
        return
    }
}

Then you add the delegate extension (below your view controller's code) and create the navigation controller / close button on the fly:

extension myViewController: UIPopoverPresentationControllerDelegate {

    func presentationController(controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
        let btnDone = UIBarButtonItem(title: "Done", style: .Done, target: self, action: "dismiss")
        let nav = UINavigationController(rootViewController: controller.presentedViewController)
        nav.topViewController.navigationItem.leftBarButtonItem = btnDone
        return nav
    }

}

Then you add your dismiss function and you should be good to go:

func dismiss() {
    self.dismissViewControllerAnimated(true, completion: nil)
}
like image 10
Travis M. Avatar answered Nov 19 '22 21:11

Travis M.