Transcript
[ Music ]
[ Applause ]
>> 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.
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.
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
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
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
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
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.
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.
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.
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.
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.
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.
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.
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
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
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
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
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.
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.
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.
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.
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.
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
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.
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
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.
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.
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
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 ]