Video hosted by Apple at devstreaming-cdn.apple.com

Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: wwdc2016-233
$eventId
ID of event: wwdc2016
$eventContentId
ID of session without event part: 233
$eventShortId
Shortened ID of event: wwdc16
$year
Year of session: 2016
$extension
Extension of original filename: mp4
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: [2016] [Session 233] Making Apps...

WWDC16 • Session 233

Making Apps Adaptive, Part 2

App Frameworks • iOS, tvOS • 37:40

In this second part of a two part series, go beyond the basics in understanding how to make your apps adaptive. Learn through practical examples how to extend your application interfaces using UIKit. Get introduced to best practices for structuring your app for flexible interface designs. Learn from the experts what to keep in mind as you build apps for the future, today.

Speakers: David Duncan, Kurt Revis

Unlisted on Apple Developer site

Transcript

This transcript has potential transcription errors. We are working on an improved version.

Good Morning. My name is David Duncan. And along with Kurt Revis we'll be talking to you today about making your apps adaptive. So, in the first part, you got to see the tools that Interface Builder has added as well as the foundation on how you can do adaptively.

But in this part, we're going to talk to you more about techniques that you can use to help make adaptive apps as well as code that you can write to build your own experiences while still remaining adaptive. So, let's look at the agenda for today. The first thing we're going to talk to you about is just some basics on sizes and size classes just to refresh everyone's memory.

In addition, we're going to talk about things that already exist in UIKit in the Tools that can help you along with Interface Builder and so you get the most out of UIKit. And finally, Kurt's going to come up and talk to you about how you can go beyond size classes to build your experiences. And so with that, let's begin with sizes.

So, sizes. If we go ahead and bring all of our devices and their orientations and their adaptations up on screen, that's pretty complicated-looking set of things. I don't think anyone wants to put all of those up, figure out what their layouts look like and make that work. So let's see if we can draw a diagram to simplify what all of this looks like.

Well, that's still pretty complicated. And I don't want you to read that because there's a lot of text in there. And it's really hard to read. So let's see if we can get rid of all that text and color and make it simpler. And it's not really helping.

So, what else can we do? Well, when we thought about this in UIKit we thought what are some good places that we can divide this space up to create good experiences for users? And as you might have guessed already, what we're talking about are size classes. We split the grid up and we split it up into the compact width and regular width, compact height and regular height.

And what we decided was that when you're in the compact space, you have a more iPhone-like experience where you try and make the best use of space. But in the regular experiences, you want to be able to do more advanced things, more interesting things with that space that really takes advantage of it.

And so that take away from that is that when you see a regular size class, that's your opportunity to make a really great experience for the user with that space, going beyond what you might do in a compact space. So, let's take a look at what UIKit already does for you based on size class.

Most views and controls don't actually change. So, you take a look at that switch. It's the same whether you're in compact or regular. That nav bar is also the same whether you're in compact or regular. But what things do change? Well, here's a presentation. It looks like it's a full screen presentation. It's on a iPhone 6s Plus.

But, if we rotate and put the device in compact, we see a form sheet. And so one thing you see is that in presentations, if you do a form sheet presentation in regular size class, if you move to a compact size class, we don't have enough room for it. And so we change that to a full screen presentation.

Now, the thing in UIKit that does most complicated adaptations based on size classes and space available is UISplitViewController. Again, we're looking at a compact width experience on an iPhone 6s Plus. And you see that, as you might have typically experienced, you just got a navigation controller where we push and pop things.

But again, if we rotate and go into compact width, we bring in the sidebar. And we think that on an iPhone 6s Plus with all of that room in regular width that having that side bar is a better user experience than not. Even though we only have so much room for it.

Similarly on iPad, the 9.7 inch iPad in landscape, you can see that we keep that sidebar up as well, allowing you to easily switch between mail messages. But what do we do when you rotate to portrait? Remember, on an iPad, it's still regular width. But we've decided that on iPad you typically have content that is much larger, much more interesting, images inline and so forth, and you need that extra space so that you can still interface with your content. However, we allow you to slide in the sidebar so you can still easily switch between email messages. And so those are some things that we do in UIKit in order to give you the best experience based off both size classes and the actual amount of space available.

And in order to take advantage of that, let's look at some best practices in UIKit to make the most out of what you have. So, we're going to speak to some things in Xcode Tools in UIKit. And in the first part, you saw Interface Builder's enhancements to building adaptive applications. Being able to easily make adjustments based on size class in gamut and other traits.

But, Xcode also provides Asset Catalogs. And Asset Catalogs are great, not just for organizing your images, but also specifying when and how they should be used. And of course, UIKit has lots of technologies that make building adaptive apps easier. We've been talking about Auto Layout for many years. There are many sessions on it if you should, you should go review those. And of course, there's a session later today on New Things in Auto Layout.

And trait collections were reviewed in the first half as well. But other things are Dynamic Type, which allows your application to adapt to the font size that users want to use. Layout Guides are a great way to pass Auto Layout information down the hierarchy. And we're going to discuss some of the layout guides that UIKit provides by default. And finally, UIAppearance is a great way to declaratively specify how you want your applications, controls and views to look. So let's go ahead and start with Asset Catalogs.

Asset catalogs allow you to adapt images automatically based on the trait environment that they're displayed in. And here's a small example of it. So we've got our picture of baby Sophia here. And we have 1, 2 and 3x versions of that image. So that whether you're on an iPad 2 or an iPhone 6s Plus, you can get the best possible image for the resolution of the device.

But another advantage of it is that Asset Catalogs lets you design for application thinning. And what that means is is that if I deploy this application to an iPhone 6s, which is a 2x device, I don't have to pay the space penalty for carrying those 1 and 3x images as well. Similarly, we'll also trim based on size classes in Gamut and a few other things. So, use the Asset Catalogs and you can make the best specification for your images and carry the least amount with you when you actually deploy to user's devices.

Other things that Asset Catalogs provide are pieces of metadata that you can attach to your image for various purposes. And the first one we're going to discuss are alignment insets. So let's bring baby Sophia back here. And let's say that we have an application where sometimes we actually want to crop this image to a square crop. And if we just naively pick the center part of the image, we'd miss most of her face. We really do want that square that frames her face well.

Now, what are you going to do? Well, you're going to bring in some measurements and figure out how far are you away from all of the edges? But, instead of having to put it in code and associate it with assets, you can do that directly in the Asset Catalog. And UIImage will vend those values back to you whenever you need them. And similarly, of course, you can create images with them. But being able to put them in the Asset Catalog means you don't have to have a giant table associating resource names with metadata.

Similarly, let's say that you have some kind of background for a table view or what have you that you need to be able to resize to adapt to whatever size it's actually displayed at. Well, you can create a nine-part image, creating slicing edges, and store those in your Asset Catalog as well. And then, when that image gets sized to its final state, it resizes nicely without having carry around large images specifically for the sizes that you're working with.

And so, that's for Asset Catalogs. Let's talk about other adaptations that you'll want to make for your application with Dynamic Type. We love Dynamic Type. It gives our users the ability to specify font sizes, and especially for our users that need a little extra help with vision, they can specify really large sizes that allow them to easily read text.

And so this year, we've made it even easier for you to adopt Dynamic Type in your application. And we've done this in two ways. The first, we made it part of the trait environment. So you don't have to listen for a notification anymore. It's just right there for your custom text views to take advantage of if you need it. But we figured you shouldn't have to do that for regular text views, for labels, text fields and text views.

So we've made those really easy to do. All you have to do is assign the font with the style that you want and set the flag that adjusts the font for the content size category. All your labels, your text fields, your text views, they'll automatically adapt to the current Dynamic Type size and you don't have to do anything.

[ Applause ]

So of course, if you're to adopt this, be certain to test your application in all the Dynamic Type sizes. As I mentioned, there's some really large ones. The Accessibility Inspector on your Mac can connect to your application and allow you to toggle back dynamically without having to go back and forth between settings. And if you're displaying these in table or collection views, you'll want to review the "What's New in Collection View" session, because there's some really great information on performance and behavior enhancements in collection view that you can take advantage to make this really great.

And so, let's talk about Layout Guides. And UIView provides two layout guides, a Margin Guide the Readable Content Guide. And we'll go through those in turn. Both of these start out with a view. Whatever content you happen to have it in. The Layout Margins Guide is defined in part by a property UIView that is the layout margins, which defines insets on all sides.

And so, how do we then create a layout guide? Well, that's just a rec specified in terms of that view. And that Layout Margin Guide, of course, provides auto layout objects for you to generate your own constraints on. Simple. So, what's the Readable Content Guide in relation to this? The Readable Content Guide is a guide that provides you with information on how to layout text so that it has a nice, readable line length. If you're on one of the new 12.9 inch iPad Pros, you can layout text from side to side and your users will kind of being turning their heads as they're reading it.

So, the first thing we want to calculate is what is the ideal width for a line of text? And then, of course, we don't want the text to flow outside of the margins. So we bring in that margins guide as part of our calculation. And we combine these two to finally form the readable content guide, another layout guide within your UIView that you can lay out text within.

Now, the Readable Content Guide is based on the dynamic text size. So, what happens when that changes? Well, we increase the dynamic type size and the ideal width increases. And as you can see, because the readable content guide is based inside of the layout margins, the guide doesn't necessarily extend past those margins. And then you can lay out your text and get a nice readable length of text that's inside of your view.

And so with that, let's talk about UIAppearance. UIAppearance, if you haven't used it, is a declarative way for you to specify what your application should look like. And what does that mean? Well, that means that instead of writing code when you see a new tab bar, for example, you write this: You say, for all of my tab bars, the appearance should have the unselected tint color is blue. Really simple. And that any time you create a tab bar, unselected items are colored blue. But it's also contextual. And that means that you can specify based on trait collections or based on the containment of those views.

And what does that look like? Let's take a look. So, here we have our little application. And as is the style, we want to have a giant header image at the top when we're in regular vertical height. And we're going to have that image just replace the nav bar's background.

However, when we're in compact vertical height, we're going to have a side-by-side layout. And the image is not going to necessarily extend to the navigation bar. So we want it to use its default background. So let's do the default background first. We create a TraitCollection for verticalSizeClassCompact. We grab the navigation bar's appearance for that TraitCollection. And we say we don't want to use any background image for that appearance. And that will cause the navigation bar to fall back to using its default appearance.

Similarly, when we do the regular vertical, we create a trait collection for that, grab that appearance, and assign an empty image, which will cause the navigation bar to construct no background image whatsoever. Now, you might note that the appearance API for trait collection, that might read a little funny. We're actually going to be changing that API for C2, so be on the lookout for that.

And so with that, let's go ahead and wrap up the best practices section where we reviewed using Asset Catalogs to organize your images. Dynamic Type to adapt to the user's desire for the font size. Layout Guides to help you build your own layouts in ways that are easily adaptable to all of your layout situations. And finally, Appearance to get your application looking exactly like you want it. And so with that, I'll hand it over to Kurt.

[ Applause ]

Thank you, David. So, if you remember from "Making Apps Adaptive, Part 1" you'll remember the takeaway message, which is: The system is going to do most of the work, so you don't have to. Now, I'm going to talk today about how to go beyond that. So, if you want to go beyond what the system provides, here's how to do it. And the operative word here is "if you want to." This isn't required. However, if you don't -- even if you don't take advantage of this, you might learn some things about how to work more effectively with UIKit.

So I'll talk about how to go beyond the basics. I'll tell you how to design your app to handle all combinations of device orientation and size. I'll talk about how you can implement these designs and change between them dynamically as your app changes size. And then I'll talk about using reusable elements to make it quicker and easier to build your app. And I'll do this in the context of an app. We'll build a real app here. So, I'll call my app My Incredibly Adaptive App. And fortunately, it's also incredibly simple. We've only got three things that we're going to show in this app.

Three items. Each one has a title. Here they're just A, B and C. And then there's a longer piece of text, a description, that goes with each one. So even though this app is very simple, I'm going to use techniques that you can use in your much more complicated and bigger apps.

So this is the model of my app. This is the data inside of it. But how should my app look and how should it act? What's the design? Well, when I'm thinking about these designs, I need to consider all these combinations. But that's far too much to do a unique design for each one. That would be far too much work.

So let's try to simplify this. I think in my app I can do it with just two designs. I'll call the first design "Tall." And I'll arrange these items vertically, A, B, and C vertically in the stack. My other design will be "Wide." So I'll arrange these items horizontally.

Now, I think no matter what size my app actually is, for my app I can use one of these two designs and make them fit. So now the question is: Given a combination of device orientation and size, how do I choose what design to use? I need to define a rule for that. And again, this is unique to my app. I think I'm going to choose: If the width is less than the height, I'll use the Tall design. Otherwise, I'll use the Wide design.

Now, I can run through all these combinations and see how it works. I can do this on paper before I even start writing any code at all. So for instance, this iPhone in portrait, the width is less than the height, we'll use the tall design. Now, I can continue that with the iPhone in landscape and with an iPad, full screen, or an iPad partial screen, and go through all the examples and try them out and make sure they make sense. So I'll reiterate what I just did. When I'm designing my app to hand these things, I thought through all the combinations. I came up with designs to cover all those combinations, the whole range. And then I defined rules to say which design to use.

So when I'm defining those rules, there's many ways I can do it. This is what makes my app unique. But you'll note that I could have checked to see if the size exactly matched certain things. I could have seen is the size 1024 by 768, therefore it's an iPad and made decisions based on that. I don't want to do that because there's too many combinations to handle. And those sizes will change over time.

So instead, I defined a simple Boolean condition that would tell me what design to use. And there's many ways we can do this. The first one, and most obvious, is just use size classes. Then we've done most of the work for you. All you have to do is check are you compact or regular.

Also, your app will act like other apps in the system because you're using the same size classes. All the Xcode tooling will help you out. You'll get a lot of things just for free. But, you can also do it yourself. For instance, I could compare a value like width or height to a threshold value. Or I could compare two values like I did, width versus height. Or I could combine these. The point is that I came up with a simple and unambiguous way to figure out what design to use.

So another thing when you're thinking about your designs is even if your size is the same, you might be, say, on one side of the iPad or the other side. So don't make your designs specific to where the buttons on the device are, or where the other app is if you're multitasking, that sort of thing.

I'll need to find the size of the app. I'll need to use those rules to decide what design to use. And then last, I'll apply that design to the UI. So change the views of my UI to make a match. Now, where should I put this code? If I make a new template in Xcode, or new view controller, it gives me this template with a viewDidLoad method. And it says do any additional setup here. So that's where I should put it, right? Unfortunately, it's not that simple.

The reason you don't want to do this is because views get loaded on demand. The first time something asks for that view in your view controller, this will get called. And that's very early. So we know at that time your view won't be in a superview yet. Layout wont' be valid yet.

So you really can't count on your view size or any parent size or traits or so on. It's just too early. So for one-time only things like initializers, loadView, viewDidLoad, you only want to put code that's going to be the same across all of your designs. A better place for me to put my code is in a view controller's viewWillLayoutSubViews.

And we'll view that because at that time the view is in a superview. Layout of all those superviews has happened. Sizes are valid. My view size is valid. Traits are valid. Everything's good. And also, this is a good time to manipulate the things inside of my view controller. So this is where I should change this stuff inside my view controller views and constraints and so on. Now, it's important to be careful here. Because this is a very hot path. This is called very often. Often, for reasons outside of your control.

So, do as little work as you possibly can in this method. Ideally you would find out what's changed since the last time it was called and then do as little as you can to update based on that change. So change only the attributes of views that you absolutely need to change.

And then finally, be careful not to cause a layout loop. If you invalidate layout of your superviews, they might in turn invalidate your layout. And soon you're finding that your app is doing but validating and invalidating layout and nothing is actually happening. So to find out more about debugging and finding out about layout loops, check out the "What's New in Auto Layout" session later today.

So, here's my two designs. We'll go back to this. How am I actually going to implement these things? Well, I think I can do it with just a view for each item, A, B, and C. And then I'll us a UIStackView to arrange these things horizontally or vertically. UIStackView will do all the work for me. I don't have to think too hard.

So here's some code. My simple example view controller is a subclass of UIVIewController. And I made a storyboard for this. It's got a stack view in it and it's already got those three views inside. Now, I am going to, in my viewWillLayoutSubviews method, I'll override that. First step, get the size, which is view.bounds.size. Second, apply my rules. So if the width is greater than the height -- or equal to the height, I will choose to use the wide design. And then finally I'll apply that design.

So, if I'm using the wide design, my stack view's access should be horizontal. Otherwise it should be vertical. That's all I have to do. So you'll note that here I'm not doing a lot of work. And I'm also taking advantage of the fact that StackView is smart. If I'm setting the access to the value that it already had, it's not going to do any additional work.

So let's see this in action. Here's my app on an iPhone in portrait. We've got that vertical layout. And now if I rotate to horizontal, we'll see that the app also rotates to horizontal. And also I got this animation for free. So I'll go back to portrait and I'll show that again.

There we are. We rotated. So, I showed this to David, because I was especially impressed that StackView gave me this animation for free. I didn't expect that. And he said, "Well, that's great. That's pretty amazing. But can you make it better? Could you make it pop?" And if you're an app developer, maybe you've heard this before from one of your clients.

So I said, "Sure, I can make this pop." I can make my app a little more interesting and I'll make it pop here. So I'll do this by, during the rotation, I'll make the items grow towards you. And then when I'm done, I'll make them go back to normal, shrink back to normal.

Now, this isn't something that we necessarily advocate that you do in your app. Not this particular technique of making it pop. But the point is where I'm putting the code and where I'm exactly doing this. So, I'm doing this code in viewWillTransition to size with coordinator. And I'm doing this because it will be called during an app size change or a rotation.

And I'm giving this coordinator that lets me set up animations alongside the rotation and to happen afterwards. Now, why don't I put all my layout code in here? That's because this isn't called the first time through when your app launches. There's other reasons, but that's the main reason.

So I'll use the coordinator and I'll set up a block to animate alongside the rotation. And all I have to do in this block is set the parameter that I want to be animated. It'll go alongside the rotation with the same curve and the same timing. And I'm going to set that stack view's transform to a scale up of a factor of 1.4. So a little bit bigger.

Then when I'm done, I get to set up my own animation with my own duration. Here I'm picking .5. And we'll go back to normal. We'll set the affine transform back to the identity. So back to normal. So here we go. Here's the same app just with that additional code on there. It'll rotate.

And it popped towards you and went back. If we rotate again, it will do the same thing. So there we go. I got to add that pop effect. I got to make it a little more interesting. But I didn't have to change my core layout code. It remained the same. So I just added it on top.

So the last thing I'd like to talk about is reusable elements. And this is a way that you can build your app out of pieces that can be reused across different designs. This way you can build you app more quickly and take advantage of these different designs without having to rewrite everything.

We'll do this by using view controllers. Each piece will typically be a view controller. And that's because they package up a lot of useful stuff all together. For instance, you get a whole tree of views, not just a single view, but a whole tree of views and all their constraints that go with them.

You get to make connections to other view controllers. So you might perform a segue to different view controller or present something, find your parent view controllers and so on. And also, it's a place to make connections to the rest of your app. So you might have a connection to model objects or an object that represents network access or so on.

Now, the view controllers in your app are going to perform different roles. You might have a container view controller. And it might contain multiple contained view controllers. Now, you're probably used to writing contained view controllers and then putting them in containers that UIKit provides. For example a navigation controller or split view controller or a tab bar controller and so on. But you can write your own container view controllers.

And this lets you really unlock a lot of power. You can do a lot of things with that. So I'll show how to do this. Here's my designs again. And in my case, I think I can have an outer container view controller that I'll call ExampleContainerViewController. And inside of that I'll have three element view controllers, one for each one of these things.

Now, thinking about it, I think I need a little refinement to my design. So if I don't have much space, I'm going to need to show just a preview of these items. I don't have room for the full text, just the title. So, when I click on one of these items, or excuse me, tap on one of these items, it's going to be a preview. I will present another view controller that shows the full text.

Then when I tap on that thing again, we'll dismiss it, it'll go away. So I'll do this by having my ExampleContainerViewController will have three small element view controllers. I'll define this later. When we present one, when we tap on that thing, we'll create and present a new large element view controller.

Now, if my app is big enough, I don't have to do all that. I'd like to just show the large element view controllers right here directly in that container. So my container will just have three instances of another class LargeElementViewController. And when the app size changes dynamically, we'll change the view controller hierarchy to go between one of these two states.

So I'll show the code here. We're going to work our way up from the contained view controllers into the containers. So we'll start with the storyboard for these contained view controllers. And it's pretty simple. We've just got a simple view with the title here. You'll note that I set the custom class to be SmallElementViewController.

And I also set the storyboard identifier to small element. This way I can find these things in the storyboard and instantiate them later. For the LargeElementViewController it's bigger. It's got that extra text in it. The same idea. I set my custom class to LargeElementViewController. And I set the identifier.

So here's the code for the SmallElementViewController. We'll start with this. And I know that every time I show this I'm going to want to tap it to show the large one. So in my viewDidLoad, this is an appropriate time to do this, I'm going to set up my tap gesture recognizer and add it to my view. Later, when that thing is tapped, this will be called. We'll go on and find our main storyboard and instantiate that LargeElementViewController using the identifier largeElement. Finally, all we have to do is present that then.

In the LargeElementViewController, it's a little more tricky because we need to know are we being presented or not. If it's being presented, we need to tap to dismiss. If it's not, we don't. So I'll put this code in viewWillAppear. I'm doing that because I can use this other method isBeingPresented to find out am I being presented or not. So if it is, then I add the tap gesture recognizer, the same way as before. When that's tapped, all I have to do is dismiss. So now let's get on to the ContainerViewController. And I'm going to add an additional object to make this easier to work with.

We'll call this the design object. And the design object is really going to wrap up all the information that describes what a design is. I'll make it an immutable struct for safety. So I can write a function that returns one of these, and after it's been returned, it won't change. Nobody can change it. And then finally, I'll allow comparisons. So I can see is my design that I want to use different than what I'm currently displaying.

So let's actually implement this. I've got just a simple Swift struct here. It's going to have two pieces of information in it. The first one is the axis. I'm reusing this enum from stackView, so it's either vertical or horizontal. Then I'm going to define my own enum for whether I'm using the small version or the large version of the view controllers inside. I call that elementKind.

Finally, I've got a read-only computed property here called elementIdentifier. And this just tells me what identifier to use to make those view controllers from the storyboard. Finally, I'll implement the Equatable protocol from Swift. This is just function equals equals. And I just compare the data inside my two designs to see if they're the same.

So finally, let's get up into the container view controller and actually do this stuff. You'll remember it's going to have three child view controllers. It's going to use those rules to create and decide what design to use. And then finally we'll update. Every layout will reevaluate what design to use and change things if we need to. So here we go. The ExampleContainerViewController. I've got an array with these three slots for three optional view controllers. And they start out as nil. Because when this thing is initially created, nothing is going to be showing there yet.

I'll also keep track of the design that I'm currently displaying. And also that's optional and it's nil because nothing has happened yet. So, in my viewWillLayoutSubviews, this should look very familiar by now. I'm going to get my size. I'm going to call a function called decideDesign based on the size, return a new design to use.

And if that design is different than what I'm currently displaying, I'm going to apply that design to my UI. So change my UI to match. So you'll note this exactly like the pseudocode that I talked about earlier. And really, no matter what your app does, you can probably follow this same pattern. You'll have different stuff in your design. Your decideDesign and applyDesign methods will be different. But you'll probably be following this exact same pattern.

So let's fill in these functions from my sample app. decideDesign will first decide on the axis. And we've already been over how we did this with this rule. For the elementKind I'm going to use a different rule. So I'm going to choose if my app's width is less than a threshold, choose to use the small ones. Otherwise use the large ones.

And here I'm just comparing against a constant. This is just an example that I'm using for my app. This isn't a value you should necessarily use in your app. It's just an example. Finally, I'm going to wrap up those two pieces of information in a design object and return it.

To apply a design, I need to apply those two things. So I need to find out if they've changed. So again, for the axis we'll just pass that directly to the stack view. It's smart. It won't do anything if doesn't need to. For the elementKind, we've got more work to do.

If we have old element view controllers and the element kind is changing, then we need to destroy the old ones and create new ones. So here's how we'll do that. We'll iterate through that array. And I'm using this enumerated function here because it lets me get the index and the object at that index simultaneously while I'm iterating.

And finally, if we've got an old element view controller, then I need to remove it. I'll call this function to remove it. Go to my storyboard, create a new element view controller using the identifier that the design gave me. And then finally call a function to add that new child view controller. And then save it in the array for the next time through.

So, last slide of code. Thank you for sticking with me. These are some things that we need to do to be a well-behaved container view controller. So it's important to do these steps in this exact order. When we're adding a new view controller, we first call addChildViewController. This adds that view controller as to self's list of view controllers.

Then we need to add that new view controller's view to the view hierarchy. We do this by using the stack view's addArrangedSubviews method. And UIKit doesn't do this for you. And in fact, can't do it for you because we don't know where it's going to go in the view hierarchy. Only you can tell us that.

Finally, once that's done, you tell that view controller it did move to a new parent view controller, and that's "self". To remove the old one, we do much the same steps, just in the opposite order. So we tell it it will move to a new parent view controller, which is nil. Remove its view from the view hierarchy. And then finally, remove it from self's list of child view controllers.

So here we are. I'll actually show you the app, finally. Here we are on the iPad in landscape. We've got lots of room. We're using that horizontal layout and we're using the LargeElementViewControllers. Now, if I slide over another app and pin it, my app's now smaller. I switched to that vertical layout. But I'm using those LargeElementViewControllers still.

In fact, they're the same instance of the view controllers that we were looking at earlier. Now, if I make that app smaller still, you'll note that we switch to that smaller design. So we're still vertical. We are using that smaller design. I can now tap on one of those small view controllers, present the large one. If I tap again, we'll dismiss it.

So there's my app. You'll note that we went beyond the basics. We really decided exactly what we wanted to do by ourselves. I handled designs of all those combinations. I implemented each design. We changed between them dynamically as the app changed size, and also used reusable elements to do it. So my app is an example of this. Your app can do all these things, too.

So now we've been through the basics. We've shown you what Xcode can do. We've shown you some of the incredible things that UIKit provides in this whole Swiss Army knife of tools. And we've been through code in a real example app. So, we've really barely scratch the surface here. There's a lot more to go. So, check out our sample code at this URL.

Also, if you didn't see Session 1, please review that. There was a great talk about inclusive app design that talked about typography. New stuff in Collection View and Auto Layout that David mentioned earlier. And then also the last two years at WWDC we've talked about adaptive apps and multitasking. And we really didn't duplicate too much of that material. So, please review that stuff. There's a lot of great material in those talks. So thank you. Have a fantastic Friday.

[ Applause ]