Transcript
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
[ Applause ]
>> JASON YAO: Wow, wow.
Good morning, everybody.
Good morning.
My name is Jason Yao.
I am an interface
builder engineer.
I am going to be presenting with
Kasia, an iOS keyboard engineer.
Welcome to the Mysteries
of Auto Layout.
As developers, you want to
build bold and beautiful apps
that will surprise and
delight your customers.
Having a great layout
will put your app ahead,
and your customers
will reward you for it.
Having to target all
the different devices
and configurations as
well as multitasking
and localization
can be a challenge.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and localization
can be a challenge.
Luckily, you have a tool
to help, Auto Layout.
Auto Layout, in its essence,
takes a large set of inputs
in the form of constraints,
transforms them into equations,
uses linear algebra to
give you a set of frames.
And that will give you the
layout that you've specified.
Now, working with Auto Layout,
you will find it's powerful
and flexible but can seem
somewhat mysterious at times.
And that's why we're
here today, to discover
and unravel the mysteries
of Auto Layout.
We have a number of tips and
techniques to tell you about,
and learning them will save
you hours of development time
and save you from frustration.
Let's get started.
If you are new to Auto Layout,
whether you're new or a veteran,
there is something
in this talk for you.
You can refer to
past WWDC sessions
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
if you need a refresher.
Let's take a look
at the outline.
This is a two-part session.
We are covering the morning
half today, right now,
and the afternoon one is in
this very same room later on.
Let's get started with
the first mystery,
mystery number one,
maintainable layouts.
Now, when you are working with
Auto Layout, you are going
to discover very soon you are
going to work with constraints.
Picture developing a
relationship between two views.
You can have a label on one
side, a bundle on the other,
specify how far apart it's going
to be, where it's going to go
into the superview,
and beautifully align
them along their baseline.
Now, whether those
specifications change for you
at runtime or design time,
Auto Layout will do its thing.
Picture a more complex example.
This is the App Store
with GarageBand showing
up as the app.
It has a number of
controls inside.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
It has a label at the top and
some buttons, it's got an icon
to one side, a segmented
control in between,
and then some preview
images at the bottom.
We can use Auto Layout
to lay this thing out
and adapt to different sizes.
And the constraints
might look like this.
Now for the maintainability
part.
Pretend that you were
not the developer
who created this layout.
But your boss comes
up to you and says,
I want a new control right
underneath the essentials
and above the star rating.
Where do you start?
You are probably going to
have to examine the controls
in the area, look
at the constraints,
try to insert your
button, break some of them,
and then reassemble it,
hopefully it all works out.
There is a more maintainable
way,
as you've guessed,
that is Stack View.
New to iOS 9 and made even
better than before on the Mac,
Stack View allows you to
arrange your views linearly.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Stack View allows you to
arrange your views linearly.
Stack View has parity
across both platforms.
It is built on top
of Auto Layout.
It manages the constraints
of your subviews for you
so you don't have
to, and it works well
when you add user constraints.
You can have a horizontal or
vertical orientation or axis,
as well as other
customizable properties,
such as the alignment.
Here you have a whole bunch
of horizontal stack views,
and here are examples
of top, center, bottom,
and baseline alignments.
There are other alignments
as well.
As well as vertical stack views.
Here are examples of fill,
leading, center, and trailing.
Now, one of the properties I
want to draw your attention
to is really cool,
distribution property,
and this one is also
new to NSStackView
as well as it's on iOS 9.
This allows you to
distribute along the axis,
and you can do some pretty
complex distribution behaviors
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and you can do some pretty
complex distribution behaviors
without having to deal
with any constraints.
Here we have examples of fill
so that we are filling
the stack view.
Fill equally, fill
proportionally, which is based
on the content size of the
subviews, and equal spacing.
And there are other
distribution options as well.
So we can build -- Thank you.
[ Applause ]
We can build this out of
stack views, so I am going
to give you a demo inside
of Interface Builder.
All right.
So I've got Xcode loaded.
With a view controller,
and to set up my scene,
all I have done is dragged
a whole bunch of controls
and images out from the Object
Library and Media Library.
You can see they are
all top-level labels,
buttons, and images.
Now we are going to go ahead and
put these things into a layout.
So first I am going to select
my button and my labels.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So first I am going to select
my button and my labels.
Then I am going to draw your
attention to this new button
on the IB canvas, the
Embed in Stack View button.
When you click on it,
it's created my stack view
and affirmed my alignment
and my axis.
[ Applause ]
Now we are going to
tune the properties.
You can see in the
Property Inspector up here
that we have the axis,
alignment, distribution,
and spacing as some
of the options,
and you'll notice
there's also a plus here.
You can make this an
iOS 9 size classable.
We are going to go ahead
and adjust the alignment,
and for the people who have
dealt with constraints before
in Auto Layout, you will
note that all we need
to do now is switch it
from trailing to leading.
Not touching any constraints.
Let's go ahead and stack a whole
bunch of other views in here.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Let's go ahead and stack a whole
bunch of other views in here.
I am going to go ahead
and click on my ratings,
stack them to horizontal --
create a horizontal stack view.
I am going to go
ahead and drag that up
into my first stack view.
Now I am going to select
the icon and the header,
click the Stack View button
for that, and then finally,
the top three controls, and
my layout is nearly there.
[ Applause ]
Now let's go ahead and
tune some properties.
We have the top-level
stack views selected.
I am going to go ahead and
first adjust this alignment not
to center but to fill,
so it's really filled
across the entire stack view.
And I am also going
to spread it out so
that it's a little more --
it's got a little more spacing.
Let's say about 20
points, maybe 25.
Then we will also add some
spacing into the stack view
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Then we will also add some
spacing into the stack view
with the labels and the buttons,
so I am going to go ahead
and click on that and
change its spacing,
and we will give it
about five points.
We also want to add spacing
between the icon and the header,
and I could go ahead and select
that stack view using
the document outline.
Or I can hold down
Control-Shift on my keyboard,
click over the icon, and get
all the views underneath my
mouse click.
So we will click on the
closest stack view to me,
the horizontal one, and increase
that to about 25 points.
Let's go ahead and run this.
You can see it's got my layout,
but we haven't positioned it
yet in the superview,
and this is
where we are going
to use constraints.
I am going to go ahead and
take the stack view and put it
against the top and
leading margins.
Then I am going to draw your
attention to this other button
on the IB canvas that brings
up the Pin menu and brings
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
on the IB canvas that brings
up the Pin menu and brings
up a pop-up for being able
to add your leading
and top constraints.
And when I hit the Add button,
it's added the constraints
for me.
Now, we also want to add to
the bottom and the trailing,
but there's another trick
in Interface Builder
that you can do.
You can control-drag
between two views.
So I am going to
control-drag from the stack view
to its superview and
select Trailing Space
to Container Margin.
That's going to add
my constraints.
For those already familiar
with the menu, check it out,
there are a few new
other features in there.
We are going to add another
constraint to the bottom,
vertical spacing to
bottom layout guide.
Now I am going to
double-click on my constraints,
set their constants so they
are flush against the edges,
and now when we run it, you
can see it fits my superview.
Something isn't quite right yet.
Take a look at that
segmented control.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Take a look at that
segmented control.
It's a little too stretched.
What is going on there?
In fact, if we rotate this,
it's a little too squished.
[ Laughter ]
At the top-level stack view,
it's looking at its subviews,
and all of them have
the same priorities,
the same content-hugging
priority
and content
compression-resistance
priorities.
We will talk more about that
later on in the session,
but for what you need to
know for now is that we need
to tell Stack View which
one is going to win.
All I have to do is select
the segmented control,
go into the sizing inspector,
choose the priority section,
go ahead and adjust my
hugging priority because I want
to have the segmented
control hug tighter,
change it from 250 to 260.
You only need to go a little
bit above its default.
Likewise, I wanted to resist
compression and not get swished,
so I am going to also increase
my vertical content compression
resistance from 750 to 760.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
resistance from 750 to 760.
Now when we run it, it's
looking much better.
Now remember - -- thank you.
Thank you.
[ Applause ]
Remember the original problem?
Our boss wanted us
to add a new control.
So here's what you do.
You tell your boss, it's okay.
I've got it.
Then you go back to your
desk, open up the storyboard,
search for the control --
in this case we will
use a button --
drag it out from
the object library,
and target which stack view
you want to drop it in.
Then you go grab a coffee.
[ Laughter and Applause ]
I would also like to point out
that Stack View handles Hidden
for its subviews really nicely.
You can set it programmatically
or through Interface Builder.
It's like it collapses or is
taken out of the view hierarchy
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
It's like it collapses or is
taken out of the view hierarchy
but is still owned
by Stack View.
So if I mark Hidden, you can see
everything adjusts accordingly.
We think you will
find this useful.
[ Applause ]
That is the demo of Stack View.
[ Applause ]
I also wanted to point out that
animation is really easy, too.
This is what it might look like.
Use a UIView Animate with
Duration block and just go ahead
and modify the Hidden
property of the subviews,
and we specially handle those.
Or you can put other
animatable properties in there
for adjustment, including
properties
on the stack view itself.
Try, for instance, adjusting
the axis so it's going
between horizontal and vertical,
and you will see
something pretty cool.
[ Applause ]
And of course, this is what
it looks like on Cocoa.
Use NSAnimationContext,
run animation group.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Use NSAnimationContext,
run animation group.
So what have we seen?
Let's take a look
at the API first.
It's simple, familiar,
straightforward.
You have your axis
or orientation,
describing whether the stack
view is horizontal or vertical.
We have seen the
distribution, alignment,
and spacing properties.
And then you have
methods for being able
to add new things to be stacked.
Add and arrange subviews.
There's also an insert
and remove.
I want to draw your attention to
the Arrange Subviews property.
This property returns you a
subset of all of the views
that are owned by
the stack view.
It returns you the views that
are currently being stacked.
What that implies is
that you can have views
that are not being stacked by
Stack View, such as decorators
or overlays, and have
a clean view hierarchy,
and we think you will
find that useful.
All you need to do is call Add
Subviews for those other cases.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
What have we seen here?
Stack View is easy to
build, easy to maintain,
we believe you are going to
compose a lot of these things,
and because Stack
View is about layout
and therefore it doesn't need
to render its own background,
we've been able to do
some optimizations.
So we have a special Transform
Layer class that doesn't render
on its own that can
make Stack View run fast
and even more performant
than your regular views.
So it is lightweight.
[ Applause ]
We went from an example
with many constraints
and a little bit of difficulty
maintaining it down to just four
and a bunch of stack views,
and this reads a lot nicer.
We feel that you can build
most of your user interfaces
with stack views, so we
recommend start with Stack View
and use constraints as needed.
And that is Stack View.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
[ Applause ]
Now, for the cases where you
need more than just Stack View,
you are going to have to --
I would like to invite up
my friend Kasia to talk
about feeding the layout engine.
[ Applause ]
>> KASIA WAWER: Hi.
My name's Kasia.
I am on the iOS Keyboards Team,
and I am here to talk to you
about proper care and
feeding of your layout engine.
It's a finicky little critter,
but it wants to do right by you.
If you treat it correctly,
it will treat you correctly.
As you have seen over of
the course of the week,
we have these new adaptive
environments as well as all
of the different iPhone
screen sizes we have.
It's critical to plan you
layout property to work
across all these environments
and not spend too
much time iterating
for every single screen.
So most people, when they are
thinking about constraints,
think of them sort of like this.
You create some views, you
create some constraints
in different ways, and you kind
of throw them into a black hole.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
in different ways, and you kind
of throw them into a black hole.
Something happens.
And a layout pops
out the other end.
Hopefully yours, possibly not.
So what I really want to do
today is try and clear up some
of this mystery here and get it
so you are hitting
your layout every time.
And we are going to start
with changing constraints.
When I talk about changing
constraints, I am mostly talking
about activating and
deactivating constraints.
Those of you who have
used Auto Layout before
and for a while may remember
we originally had the concept
of adding and removing
constraints to and from views.
Well, don't do that anymore.
Activate and deactivate
is much better.
It's a class method on NSLayout
constraint for both of them,
and there are some
really big benefits
to using activate
and deactivate.
Constraints find
their own container.
They are more efficient.
And you don't need
to own all the views
in your layout in
order to use them.
So this all really boils
down to just don't use add
and remove anymore.
Use activate and deactivate.
So there are a few other
things to keep in mind
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So there are a few other
things to keep in mind
when you are thinking about
changing your constraints.
Number one, never
deactivate everything
in self.view.constraints.
If anybody has tried this,
you've probably seen some
really odd things happen.
Not all the constraints in
that array belong to you,
such as the ones the view
uses to set itself up,
so weird things are
going to happen.
Just please don't.
We will see why in a moment.
What you need to do instead is
keep references to constraints
that you may need to change
later, either in an array
of constraints or as
individual constraints
so that you can manage things
exactly the way you want to.
So let's take a look at
changing constraints.
So I have here an iPad app
that I've made with a bunch
of planets, and it's
been set up for iPad
and it also has an iPhone mode,
so it should work well
with multitasking.
Except that I am not actually
removing any constraints,
so when I move from my nice
vertical compact layout back
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
so when I move from my nice
vertical compact layout back
to my regular layout,
I have lost all
of my nice curve
that I had before.
So the first thing I might
be tempted to do to try
to rectify this is to remove all
of the constraints
in the superview.
And when I do that, I
am also going to need
to reactivate any
shared constraints.
But you will see all sorts of
exciting things happen here
if you try to take
this approach.
So here's my regular
layout again.
That's nice.
And here it is still nice.
Hmm. That's not good.
And if I move back to
the regular layout, well,
I didn't actually want to make
the planets quite that giant,
so this is not really
what I want to do.
So how do we do this properly?
I am going to take a moment to
grab this code so you don't have
to watch me type it all out.
So in your trait collection
did change, what you want
to do is check and see with each
environment whether the other
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to do is check and see with each
environment whether the other
set of constraints that you
might need to change are active.
Here I have two arrays
with eight constraints each
that differentiate between
that vertical alignment
with the planets.
So if I check and make
sure that that's active,
remove the ones -- or deactivate
the ones I don't want anymore
and activate the new ones,
it should look much better.
Regular. Still regular.
Compact. And regular again.
So that's where I
actually want it to be.
[ Applause ]
Oh, thank you.
So a cool thing in
Auto Layout is
that you can actually animate
constraint changes as well.
Here I have a double-tap gesture
to animate between this regular
and compact layout by dropping
that activation and deactivation
into an animation block
along with a call of layout
if needed to the superview.
So you'll see if I double-tap,
I can move really nicely
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So you'll see if I double-tap,
I can move really nicely
between the set of views with
just a couple lines of code.
And if you want to get fancy,
you can keep references to all
of those individual constraints
and use keyframe animations
to really kind of spice
things up in your app.
So that's activating
and deactivating
constraints properly.
So what do we know about
changing constraints?
Well, number one,
never deactivate
self.view.constraints.
We know that very well now.
Keep references to
constraints are going
to change in the future.
It will make everything
work better,
and you can do cool
things like animation.
And you can also animate
changing constraints
by just dropping it into
a View Animation block,
which is pretty cool.
So we are still at
the kind of basic part
of building our layout,
where we still have our views
and constraints and are putting
them into the layout engine.
But if we give the layout
engine more information,
we can get a lot better
results that work properly
in all these different
adaptive environments.
So we have kind of positioning
for all of our constraints,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So we have kind of positioning
for all of our constraints,
and now I want to
talk about size.
So mystery number
three, view sizing.
There are a couple different
ways to size your views,
and you no longer have to say,
I want a view that's 200
points wide, 300 points tall.
Instead, you can
use different ways
to create your constraints
adaptively to, you know,
work in all of our various
environments we have
for you now, and we start with
intrinsic content size on that.
Many views have this, such
as labels and image views,
and it's basically the
size that the view would be
if it was just right
around its content.
And the engine looks at this
and tries to solve your layout
at this size, and it will
actually create the internal
size constraints for you.
But it can't guarantee the
layout size because of stuff
like adaptivity, it needs
to be able to stretch
and shrink views a little bit.
So around things like this,
the user won't see it,
but you might have
a few extra points
of padding here and there.
So you can use this in order
to lay out your views in a way
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So you can use this in order
to lay out your views in a way
that means you don't
have to specify
that this label is this wide.
It will simply be as
wide as its content.
But if you need to define
a particular view size,
start with constraints.
It's very easy to
do things like,
this view should be half
the width of my superview.
This one should be
three-quarters
of the height of my superview.
This one should be equal
to the view next to it.
You can, however, override
that intrinsic content size
if you absolutely need to.
There are only a couple reasons
you might need to do that.
One is if you can't get
the size information
from the constraints.
And another might be if you
are using custom drawing
in your view.
But a lot of times you can still
just set up the constraints
for that view, and you
won't have to do anything
with overriding intrinsic
content size.
If you do override
intrinsic content size,
you are responsible
for invalidating it
in case the slide class
changes or the text changes
that you've got dynamic
text, localization,
or anything like that that
may cause your view to need
to recalculate its
internal size.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to recalculate its
internal size.
So let's look at setting
up a view with constraints
without using any
particular point sizes,
all in relationships.
Here we have a standard
image view.
So what I want this to
take up most of my view,
so I want the width to be
equal to three-quarters
of the view that it's in.
That means that every
environment it's in,
the width will be
three-quarters of that view.
So I set that up first.
And then I can take that
proportion that I now have
from calculating from the
superviews with and multiply it
by one and a half
to get the height.
And this will give me
a consistent layout
across all devices and
all modes in multitasking.
So view sizing is important,
and the more you rely
on proportions, the more likely
you are to have a good layout
that will really, you know,
be easy for you to put
across all these different
screens we are giving you.
And one of the things that a
lot of people when they think
about sizing views think
about is self-sizing views,
and most particularly
self-sizing table view cells.
And these are cells that get
their height from their content
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And these are cells that get
their height from their content
so that you don't have to
have one size of cell all up
and down your table view.
You can have them in
different sizes depending
on what's inside of them.
And you don't actually have
to calculate every
row height yourself.
The constraints can
help you with that.
So self-sizing needs to get
its size from your constraints.
Again, this doesn't mean points.
This means proportions
and relying on things
like intrinsic content size
to help you sort of do that.
Width is defined with table view
cells already, so you just need
to worry about height.
And you can take advantage
of proportions to do this.
Here we have a table view cell.
The label should be twice
as wide as the image view,
and they have a little bit
of padding between them.
So when I have a label that's
taller because it has more text,
the table view cell grows in
order to accommodate that.
So let's look at a quick
demo of how that's done.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Okay. So here I have my table
view cell that I have set
up the horizontal
constraints for already.
If I were to just build
and run it the way it is,
it would not be exactly
what I was hoping for.
So everything is
kind of squished.
My estimated table
view height is 60,
so it's drawing it
there by default.
So what I want to do now
is tell the table view cell
that if the label
grows, it grows with it.
And I do that by putting their
top and bottom anchors together.
So then the label is going to go
ahead and push against the size
of the table view cell
and make it taller.
So top space and bottom
space, to container margin.
Let's see what we get here.
This is better.
But it's still not
quite what I wanted.
Because we have cells
like this one
where the image is now cut off
because the label
isn't tall enough.
So to fix this, we can
take advantage of the fact
that views can stretch beyond
their intrinsic content size
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that views can stretch beyond
their intrinsic content size
and tell the label
that it should be
at least as tall as the image.
And so the label will get
some extra padding around it
that will allow it to -- to
start at the height of the image
and stretch beyond that
when it's tall enough.
So you don't see
that extra spacing,
and the text stays
nice and centered.
But the label is still
what's defining the height
of the table view cell.
And now that looks much better.
All of my cells are
properly sized,
and I can see everything
I wanted to see.
And that's really
all that's involved
in creating self-sizing
table view cells.
Just making sure that
everything is fully defined.
[ Applause ]
Thanks. Okay.
So view sizing.
What have we got?
Certain views have an
intrinsic content size.
You can use that when you
are defining relationships
to other views.
Constraints should define
your size when possible.
When not, you can override
intrinsic content size,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
When not, you can override
intrinsic content size,
but make sure you invalidate
intrinsic content size
when you do so.
For self-sizing views,
you just need
to define the size fully
in your constraints.
Where are we building
the layout?
We have our original
information.
We are going to add
some sizing constraints.
From the views we
just put together,
their intrinsic content sizes.
What's next?
How do we help layout
engine know what we want?
We give it some priorities.
This is mystery number 5.
If you have been doing
Auto Layout for a while,
you may have landed
in a situation
where views aren't landing
where you expect them
to in regard to their superview.
Maybe on each build and run
it's a little bit different,
or if you rotate or resize it
gets a little bit different.
So that's called ambiguity.
And it can happen for a
couple different reasons.
One is because you don't
have enough constraints
in the direction
you are looking for.
So for instance, if I had Saturn
here kind of where I wanted it
in the middle, in this dotted
line, but when I actually ran
that app it ended up way
off to the left or right,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that app it ended up way
off to the left or right,
that might mean that I
don't have enough horizontal
constraints to tell the
engine where to put Saturn.
Vertically it seems
fine, so that's good.
And the other thing that
can cause this is equal
nonrequired priorities.
Equal required priorities often
calls unsatisfiable constraints
if the layout engine can't
solve your view that way.
Equal nonrequired priorities
means the engine has
to make a choice for you.
And it tries really hard
to make a good choice,
but it doesn't really
know what you want.
So you just need to give it
a little bit more information
to get there.
So how do priorities work?
They go from 1 to 1,000, and
we have a couple of constants
to help you balance
your priorities.
One thousand is layout
priority required.
Default high is 750,
and default low is 250.
And they look like this in
a visual format language,
you simply put an @ symbol
before the number you wish
to use.
In Interface Builder,
you can stick it right
into a text field here.
And if you are using explicit
constraints, it's a property.
And that's where you can
actually use these constants
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And that's where you can
actually use these constants
or numbers or a combination
of the two.
The highest priority, when
they are competing, wins.
And we'll look at
this in the context
of content priorities
in a moment.
But an important note.
The system has some
priorities that it uses to lay
out its views internally.
On iOS, we have system layout
size fitting size at 50,
and there are several window
priorities around 500 --
490, 500, and 510
-- that OS X uses.
If you look at the
NSLayoutConstraint header,
you will see what
those numbers are.
If you set those
equal to each other,
you may end up in these
ambiguous situations
that you're about to see.
You want to use something
that's a little higher or lower
than the priorities
the system is using.
Content priorities.
These are how a view
handles its content.
By default, they are
set as Not Required,
and you should not
set them as Required.
Content needs to be able
to move a little bit.
We have dynamic text.
We have localization.
We might be able to have users
choosing different images,
stuff like that.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
stuff like that.
It needs to have a
little bit of wiggle room.
You can't say it always
has to be this size.
You will end up with
unsatisfiable
constraints eventually.
But equal priorities that
are not resolved can cause
ambiguity, as I said earlier.
Let's look at an example of
this using content-hugging
priorities, which is
one of the two types
of content priorities
that we have.
Here we have a text
field and button,
and the text field is all
squished off to the side,
and the button is
all spread out.
That's because they both have
a content-hugging priority
at their default, which is 250.
The engine needed
to make a choice,
and it wasn't sure whether
the answer was this or this.
So you can actually tell it what
answer it should choose every
time instead of getting one
or the other depending
on your environment.
So if you bring the button's
content-hugging priority
down to 249, you will
get the first layout.
And this is because the engine
looks at your constraints
and your priorities
and says, oh,
the text view hugging
priority is fairly important,
but the button hugging priority
is not at all important,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
but the button hugging priority
is not at all important,
so I can go ahead and stretch
that away from its content
to fill up this horizontal
part of your view here.
Meanwhile, if you
take the button
and you put its content-hugging
priority above that
of the text field, you
will get the other solution
because the engine says, hey,
now I want to hug this
button text really closely,
and that means that I can
stretch the text field
in order to solve your layout.
And that's really
all that's required
to adjust an ambiguous
situation like that.
The other type of
content priority is
compression resistance.
And this is how much a
view resists its content
getting squished.
So if you have -- squished is
a technical term, by the way.
Feel free to use it
whenever to impress people.
Compression resistance
priorities for image views
and labels in the horizontal
direction default at 750,
and that's a very high priority
because generally you want
to see all of your content.
But what if you localize
this app?
And you had these two
things that were competing,
and you had a language that
used a much longer word for
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and you had a language that
used a much longer word for
"red," such as Polish.
It could get clipped.
This is one of the choices
that the engine has to make.
It can either shrink the
image view or clip your label.
It doesn't really know
what you want it to do,
so it makes that choice.
If you wanted the label to
always show all of its content
and it was okay to shrink that
image a little bit, again,
all you have to do is set the
labels content compression
resistance priority
slightly higher than that
of the image view, and now
you can see the whole view.
So as you localize, that's the
adjustment that's made in order
to make sure that all
of your text is visible
to your audience.
So those are priorities.
They can help you
solve unsatisfiability,
which is that thing that causes
that horrible log that you see
when you build and run
and everything is broken.
So they are really helpful,
but you want to look
out for competing priorities.
If they are competing, you can
get ambiguity, as you just saw.
Results are more
consistent across screens
and windows when you do this.
If you use content
priorities well,
you can get to the right layout.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
you can get to the right layout.
Sometimes all that's
needed is adjusting hugging
or compression resistance
a little bit.
Now in building our layout,
we have added some priorities
for your constraints
and your content.
So what else do we need in order
to finish solving this layout?
Alignment.
Again, if you have been using
Auto Layout, you are familiar
with aligning your views.
They need to have some sort
of horizontal alignment,
so they know how to relate
to each other, and some sort
of vertical alignment.
I am going to talk specifically
here about aligning text.
So first of all, with text
views, we have the concept
of a baseline, and
this is the line right
under the bottom of the text.
Text views have a first baseline
and a last baseline alignment
thing, where you can --
the first baseline runs right
under the first line of text,
and the last baseline runs right
under the last line of text.
A single-line text view has
these two equal to each other.
So it aligns text
better than top or bottom
in many circumstances
and helps with stuff
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
in many circumstances
and helps with stuff
like dynamic text sizing.
It gives you better control
over changing views as well
if something needs to grow.
For instance, if you have
a label next to a button
and they are aligned by the
bottom, the button's frame
in this instance
is a little large
because it's filling
in some extra space.
So if I add a couple of lines of
text to it, the button can sort
of end up in a nebulous area.
And I might want to center align
that, or it might be better
to have it look more
something like messages
and have it sit right
at the bottom next
to that last line of text.
So if I just align them by
last baseline, no matter what
that text view does, the
button will stay aligned
with that last baseline.
And of course, you can also
do it for the first baseline
if that's the sort
of view you desire.
So as you have a growing
view, it's staying aligned
with the views around it.
The other important part of
text alignment is leading
and trailing.
This is actually important
for all types of views,
but its big use is
for localization.
If you are in a left-to-right
language
when you are writing
your app and you use left
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
when you are writing
your app and you use left
and right constraints for your
layout and it gets localized
to a language that uses
right-to-left, you're going
to get some odd results.
It will look something
like this.
Everything sort of
flips in place,
but it no longer reads properly.
I was reading from the image
view over through the text,
and now I am reading the
text from the other direction
and it's going over
to the image view.
That's not really what I wanted.
If you use the exact same
constraints with leading
and trailing alignments instead,
leading for left and trailing
for right, you would end
up with the correct layout,
where everything actually reads
exactly the way you originally
designed it when you switch
to a language that goes
in a different orientation than
the one you were starting with.
So use leading and
trailing pretty much always.
There might be some
circumstances
where you still need
to use left and right.
I am not familiar
with any right now.
I am sure that you
guys have a few.
But if you do use
left and right at all,
be careful when you are mixing
and matching that with leading
and trailing because, again,
when that environment changes,
you might end up with
unsatisfiable constraints.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
you might end up with
unsatisfiable constraints.
Okay. Final piece
of our alignment puzzle
is alignment rects.
Alignment rects are important
because it's what the
engine actually calculates.
The engine takes all
of your constraints,
calculates the alignment
rects, and uses them
to actually lay out your views.
So what are alignment rects?
They are usually the frame
of the view, but not always.
Instead of just being the
frame around the view,
it includes the critical
content of the view,
which is what you
really want to align.
Here we have a checkbox,
for instance,
where if you were doing
a center x alignment
with some other view, you
would probably want to align it
by the center of the
circle and not by the center
of the checkbox,
the whole check,
which would be a
little bit too high.
Same thing with a
button that has a shadow.
It kind of gets rid of those
little extra bits and focuses
on the critical part
of the content.
It doesn't change when the
view transforms, either.
So when you lay out your
view and want to emphasize
or deemphasize something,
it won't mess up the rest
of your layout to throw
a transform in there.
If you do need to change these,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
If you do need to change these,
which you should very rarely
need to do, we have an override
for alignment rect
insets you can use.
And that basically means you
can hand the engine, say, oh,
whatever alignment rect
you are calculating,
inset it by this much
and use that instead.
You will very rarely if ever
need to do that in your layout,
but it is there if you need it.
To find out what the engine has
actually calculated for you,
you can draw it on your view
using the view debugging option
in the Debug menu of Xcode
and just selecting Show
Alignment Rectangles.
This will draw yellow alignment
rectangles on your view.
You can also get it from the
debugger using alignment rect
for frame on specific views.
We are going to talk about that
more in Part 2 when we talk
about debugging layouts,
so definitely tune in there
if you want to learn more
about how all this works.
So for alignment, we've learned
that text is a little bit
different than everything else.
You want to make sure that you
are ready for both dynamic text
and localization when you
are preparing your app,
and Auto Layout can
really help you with that.
You also have this possibility
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
You also have this possibility
of overriding alignment
rect insets
if you have very interesting,
specific views that need to sit
in a very specific frame.
So where are we in our layout?
Okay. We have now alignment,
and if we needed to,
we overrode a couple of
alignment rect insets.
Now we are ready to let
the engine do its work.
So let's throw everything
in there.
The layout engine
will think about it,
calculate your alignment rects,
and then that will be used
to actually build the layout.
Now you have a very happy layout
that's exactly what you wanted
for every screen size.
Trust me, this is your
layout that you wanted.
Okay?
[ Laughter ]
So let's see.
Let's summarize what
you've learned today.
Stack views are a great way
to build a maintainable layout
that will work well across
all of our devices and sizes.
And you can use -- if you use
activate and deactivate properly
for your constraints, you
can get really dynamic,
interesting layouts that require
less work than having to design
for every single screen.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
for every single screen.
When you determine specific
sizes, use constraints.
Override intrinsic content size
only when absolutely necessary.
Use priorities to get those last
little adjustments together.
Finally, alignment goes beyond
top, bottom, and center,
especially when you are looking
at text, keep localization
and dynamic text in mind.
For more information
about all of this,
we've got some documentation up.
You can download that planets
sample code if you want to play
around with animation,
and of course,
you can go to the forums or, in
a pinch, contact our Evangelist.
Tune in this afternoon for
Mysteries of Auto Layout Part 2,
where you will learn more
about the layout cycle in depth
and more information
about debugging a layout that's
not doing what you want it
to do.
We have some other
related sessions here.
Have a great day.
Thank you.
[ Applause ]