Using Adaptive Transitions in iOS 8

Hey Party Peoples! I've been writing a new app, all iOS 8-only. Gonna use Swift! Gonna use adaptive segues! It's gonna be modern, it's gonna be cutting edge! No more separate iPhone/iPad storyboards for this guy. I've watched my WWDC talks, I'm good to go! Well hold on there space cowboy!

Let's talk a bit about popovers. I'll assume you've watched 214 about View Controller Advancements, you've watched 216 about Building Adaptive Apps, and you've watched 228 about Presentation Controllers. There are several talks that all say something like "and you can use an adaptive popover segue and it will work on an iPhone. You need to add a back button which we can't go into here, but go watch 228 for more on that." 228 seems to be the endpoint for popover knowledge and 288 does something very subtle that … I don't think the intent was to mislead but it led me astray. I watched the key bit several times before I caught the omission.

The main glitch, to my mind, is that the popover example (the "Important People" app) isn't using storyboards and that they show code snippets on the slides but they don't provide the entire source to the Important People app. There is a point around 9 minutes in where they show flipping between a compact and regular horizontal size class and how the view is a popover in regular and fullscreen in compact. Of course, they couldn't talk at WWDC about why you cared but we know now: this is what happens when you rotate a iPhone 6+ into landscape.

Here's the transcript from asciiwwdc.com:

So to do adaptivity with popovers is fairly simple.

Going back to Peter's code that we left off with is that this code doesn't actually change.

All through that, this was the code that presented that popover.

But to influence how the popover actually works, we need to set a delegate on the popover Presentation Controller.

And we need to implement just two methods.

And in the slides they show us the two methods and it seems great! Except if you do that in your app it won't work. Plus the sample code is weird. They create a UINavigationController up from scratch and make the root controller the presented view. How do you ever get out back out of that? Where did that weird Dismiss button come from in the first place?

Look at that transition when they demonstrate the "Dismiss" button very carefully. It isn't a push transition, it is a Cover Vertical modal presentation. That's why the button isn't a "Back" button and it's not on the left. That's the rub: I'm pretty sure they manually built a button and stuffed it in the navigation item but they never show that code. If you blindly "just implement two methods" and drop in the code they show in the slide you will be sad. You get stuck when you run in compact horizontal (ie: any phone in portrait) where the view is full screen and there's no way to dismiss it. The missing code is simple but I think the presentation inadvertently implies the runtime provides it and it does not. Here's my Swift version:

// Clipped from viewDidLoad
    navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Dismiss",
        style: .Plain,
        target: self,
        action:"dismiss")

func dismiss() {
    dismissViewControllerAnimated(true, nil)
}

I was naively assuming we had a UINavigationController at root and the popover segue adaptively became a push. It does not. I don't think you can do that. Popovers adaptively become modal presentations and thus you need to dismiss the view controller when you're done. (It doesn't have to be a Cover Vertical transition. It will use whatever modal transition you set on the destination view. You can use a flip or whatever if you'd prefer.)

I had a version of my where the storyboard had the root UINavigationController and I had both the push and the popover transition in the storyboard and checked the size class at run time to select the proper segue. It worked, but you couldn't rotate the device after presentation and have it change adaptively. The code example above, plus the code from the slides in 228 does exactly that. On the 6+ the screen will flip between a full screen with a "Dismiss" button and a popover fluidly as you rotate.