Ios – Close button on adaptive popover

iosobjective cuikituistoryboard

In a storyboard I have a root view controller with a button which triggers a 'Present as Popover' segue to a UINavigationController containing a UITableViewController. I want the nav controller to be present on both iPhone and iPad.

On an iPad, this works great in a popover.

On an iPhone, I get the modal presentation, so now I need an additional bar button item to dismiss the modal view. From watching the WWDC videos, I've attempted the following in the root view controller:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    UIViewController *vc = segue.destinationViewController;
    vc.popoverPresentationController.delegate = self;
}

- (void)dismissPopover {
    [self dismissViewControllerAnimated:YES completion:nil];
}

- (UIViewController *)presentationController:(UIPresentationController *)controller viewControllerForAdaptivePresentationStyle:(UIModalPresentationStyle)style {
    UINavigationController *nvc = (UINavigationController *)controller.presentedViewController;
    UIBarButtonItem *bbi = [[UIBarButtonItem alloc] initWithTitle:@"Done" style:UIBarButtonItemStyleDone target:self action:@selector(dismissPopover)];
    nvc.topViewController.navigationItem.leftBarButtonItem = bbi;
    return nvc;
}

I understand the -presentationController:viewControllerForAdaptivePresentationStyle: method should only get called when the UI is adaptive i.e. modal, however it doesn't get called at all, even when running as a modal on iPhone.

Best Answer

Here's the Swift version of Nick's correct answer for those who want a quick cut and paste.

Note: This is to create a popover on your iPad but a modal sheet with a close button on your iPhone.

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

The below code 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 prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "myPopoverSegueName" {
        segue.destination.popoverPresentationController?.delegate = self
        return
    }
}

Then you add the delegate extension 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: #selector(self.dismissPopover))
        let nav = UINavigationController(rootViewController: controller.presentedViewController)
        nav.topViewController?.navigationItem.leftBarButtonItem = btnDone
        return nav
    }

    @objc private func dismissPopover() {
        dismiss(animated: true, completion: nil)
    }

}
Related Topic