Best practice dismissing iOS 9 adaptive popover segue in compact size?

I am migrating legacy popover / navigation push controller code, and I'm trying to work with the new storyboard adaptive Popover segue. I'm finding myself wishing it didn't pose itself modally in compact size. I would rather it gave me options to push the new controller onto my existing navigation controller when in compact size, like my old UI did for iPhone idiom, or let me specify a different navigation controller for each size class (one with a Done button, and one without). If I allow the modal behavior.


I feel like I have two obvious use cases that the new adaptive Popover segue appears to miss (or at least, requires adding code).


What is the best practice for working with the new adaptive Popover segue in compact size? Should I override the adaptive segue class, and change the behavior for compact size, or is there some other alternative?

Replies

You have a couple of choices I figure:

  • You could force the popover never to adapt. Implement -[UIAdaptivePresentationControllerDelegate adaptivePresentationStyleForPresentationController:] and[UIAdaptivePresentationControllerDelegate adaptivePresentationStyleForPresentationController:traitCollection:]to return UIModalPresentationNone.
  • You could put two different segues into the storyboard, both starting and ending the same view controller, then trigger them programmatically depending on the current trait collection. One segue could be a Show and the other a Popover for example. If you then need to interpose a new navigation controller, you could do that with one of the segues e.g. source -> Popover -> navigation controller -> Root -> destination and source -> Show -> destination.
  • You could use the Show adaptive segue in storyboard, then implement -[UIViewController showViewController:sender:] to detect the destination view controller and do a popover instead.
  • You could write your own segue. Lots of code.
  • You could forgo pushing the view controller and just let the popover adapt.
    • You can try to detect when it is displayed in a popover and when not, and show/hide your Done button, although in practice I've found this very problematic -- in particular, the sensible callback -[UIAdaptivePresentationControllerDelegate presentationController:willPresentWithAdaptiveStyle:transitionCoordinator:] only works with 8.4 and I still had issues getting this to work reliably. In the end, I just showed the Done button always.
    • There's much better support for replacing or adding a view controller via -[UIAdaptivePresentationControllerDelegate presentationController:viewControllerForAdaptivePresentationStyle:], if that floats your boat.


Hope that helps.


Glen Low

Pixelglow Software

get.instaviz.com

Most of these options went slightly above my head, because I'm trying very hard to keep as much of my segue logic in the storyboard rather than in code, and I don't have good sample code to work from (stackoverflow tends to favor pre-iOS-9). For example, I couldn't figure out how to make my source view controller a presentation controller delegate for the segue in the storyboard; perhaps I still have a lot to learn about storyboards.


What I wound up doing instead was, to forget about adaptive presentations, always embed in a navigation controller and conditionally add a Done button on the target view controller in prepareForSegue, when the source controller is horizontal size class Compact:


class MyViewController {

    func dismissCompactPopoverPresentationController() {
        self.dismissViewControllerAnimated(true, completion: nil)
    }
   
    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        if segue.identifier == "myPopoverSegue" {
            let navigationController = segue.destinationViewController as! UINavigationController
            let myDestinationViewController = navigationController.topViewController as! MyDestinationViewController
           
            // Other segue setup for my destination view controller
           
            if self.traitCollection.horizontalSizeClass == .Compact {
                myDestinationViewController.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .Done, target: self, action: "dismissCompactPopoverPresentationController")
            }
        }
    }
}


This appears to work for the use case where I have a modal destination view controller and want to "escape" out of it, either by tapping outside of the popover in Regular size class, or by tapping Done in the navigation controller in Compact size class. I haven't tried to solve the "would prefer a push instead of a modal popver" use case (I might abandon that in my UI now that I have the above workaround).