Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to know if a view controller will be presented as a popover or modal?

Before presenting a view controller I set the modalPresentationStyle property to UIModalPresentationPopover. This will present the view controller as a popover when running on devices with a regular horizontal size class (iPad and iPhone 6+ in landscape) and as modal/fullscreen on other devices. It's also possible to override this behaviour by overriding adaptivePresentationStyleForPresentationController so that the view controller is presented as a popover on all devices.

I wonder if it's possible, after a view controller is presented, to know if it's presented as a popover or not? Just looking at the size class won't do it as it's possible that the view controller overrides adaptivePresentationStyleForPresentationController.

The obvious answer would be that I, as the programmer, should know if I override adaptivePresentationStyleForPresentationController or not but I want to write a function that can determine this in runtime for any view controller by passing in the view controller or maybe the UIPopoverPresentationController (or any other object needed) as an argument.

Here's some code to present the view controller:

navigationController = (UINavigationController *)[MVSStore sharedInstance].addViewController;
navigationController.modalPresentationStyle = UIModalPresentationPopover;
[self presentViewController:navigationController animated:YES completion:^{}];

UIPopoverPresentationController *popoverController = navigationController.popoverPresentationController;
popoverController.sourceView = self.view;
popoverController.sourceRect = CGRectMake(20, 20, 20, 20); // Just a dummy
popoverController.permittedArrowDirections = UIPopoverArrowDirectionAny;

Here's the current code to detect if the view controller is presented as a popover or not. But as mentioned above it just looks at the size class which doesn't work for all cases.

+ (BOOL)willPresentTruePopover:(id<UITraitEnvironment>)vc {
    return ([vc traitCollection].horizontalSizeClass == UIUserInterfaceSizeClassRegular);
}

I cannot find any property or function in UIViewController or UIPopoverPresentationController (or anywhere else) that gives me this information right away but maybe I'm missing something?

like image 688
Markus Avatar asked Sep 14 '15 10:09

Markus


2 Answers

You said you were trying to do this to remove the cancel/done button. Instead, only add the button in the case when it is needed.

The official way to implement this is first remove the Done button from your view controller and second, when adapting to compact embed your view controller in a navigation controller, now add the done button as a navigation item:

func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
    return UIModalPresentationStyle.FullScreen
}

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

func dismiss() {
    self.dismissViewControllerAnimated(true, completion: nil)
}

Full Tutorial

Screenshots

like image 60
malhal Avatar answered Sep 21 '22 02:09

malhal


I'm attacking it in the same way you were, in that I set up the Done button with its target-action in Interface Builder. In order to remove it, I was testing if the popoverPresentationController != nil. On my testing device (iPhone 5 running iOS 10), this test successfully ignored the iPhone while executing on an iPad Pro running iOS 11. I ran into problems while testing it on an iPhone 8 running iOS 11. It appears in iOS 11 that a popoverPresentationController is now instantiated even when presenting the view modally. As a result, I am just testing the horizontal size class of the presenting view controller. Not sure if that's the correct way, but it works for me, since I can't find any way for the popoverPresentationController to tell me it is actually presenting modally.

    weak var ppcDelegate: UIPopoverPresentationControllerDelegate?

...

    if popoverPresentationController != nil && 
        popoverPresentationController!.presentingViewController.traitCollection.horizontalSizeClass == .regular {

        navigationItem.rightBarButtonItem = nil
        popoverPresentationController?.delegate = ppcDelegate
    }
like image 37
Dan M. Avatar answered Sep 24 '22 02:09

Dan M.