Search code examples
ios8uipopovercontrolleradaptive-layout

iOS 8 presentationController determine if really is popover


I'm using the new adaptive "Present As Popover" capability of iOS 8. I wired up a simple segue in the StoryBoard to do the presentation. It works great on an iPhone 6 Plus as it presents the view as a popover and on an iPhone 4s it shows as a full screen view (sheet style).

The problem is when shown as a full screen view, I need to add a "Done" button to the view so dismissViewControllerAnimated can be called. And I don't want to show the "done" button when it's shown as a popover.

enter image description here

I tried looking at the properties of both presentationController and popoverPresentationController, and I can find nothing that tells me if it is actually being shown as a popover.

NSLog( @"View loaded %lx", (long)self.presentationController.adaptivePresentationStyle );          // UIModalPresentationFullScreen
NSLog( @"View loaded %lx", (long)self.presentationController.presentationStyle );                  // UIModalPresentationPopover
NSLog( @"View loaded %lx", (long)self.popoverPresentationController.adaptivePresentationStyle );   // UIModalPresentationFullScreen
NSLog( @"View loaded %lx", (long)self.popoverPresentationController.presentationStyle );           // UIModalPresentationPopover

adaptivePresentationStyle always returns UIModalPresentationFullScreen and presentationStyle always returns UIModalPresentationPopover

When looking at the UITraitCollection I did find a trait called "_UITraitNameInteractionModel" which was only set to 1 when it was actually displayed as a Popover. However, Apple doesn't provide direct access to that trait through the traitCollection of popoverPresentationController.


Solution

  • The best way (least smelly) I've found to do this is to use the UIPopoverPresentationControllerDelegate.

    • Ensure the presented view controller is set as the UIPopoverPresentationControllerDelegate on the UIPopoverPresentationController being used to manage the presentation. I'm using a Storyboard so set this in prepareForSegue:

    segue.destinationViewController.popoverPresentationController.delegate = presentedVC;
    

    • Create a property in the presented view controller to keep track of this state:

    @property (nonatomic, assign) BOOL amDisplayedInAPopover;
    

    • And add the following delegate method (or add to your existing delegate method):

    - (void)prepareForPopoverPresentation:(UIPopoverPresentationController *)popoverPresentationController
    {
        // This method is only called if we are presented in a popover
        self.amDisplayedInAPopover = YES;
    }
    

    • And then finally in viewWillAppear: - viewDidLoad: is too early, the delegate prepare method is called between viewDidLoad: and viewWillAppear:

    if (self.amDisplayedInAPopover) {
        // Hide the offending buttons in whatever manner you do so
        self.navigationItem.leftBarButtonItem = nil;
    }
    

    Edit: Simpler method!

    Just set the delegate (making sure your presentedVC adopts the UIPopoverPresentationControllerDelegate):

    segue.destinationViewController.popoverPresentationController.delegate = presentedVC;
    

    And supply the method:

    - (void)prepareForPopoverPresentation:(UIPopoverPresentationController *)popoverPresentationController
    {
        // This method is only called if we are presented in a popover
        // Hide the offending buttons in whatever manner you do so
        self.navigationItem.leftBarButtonItem = nil;
    }