Introduction to Auto Layout for iOS and OS X

Session 202 WWDC 2012

If your application has a user interface, then this session is for you. iOS 6 adopts OS X Lion's powerful constraint-based layout engine, making it easy to design a flexible user interface that responds dynamically to layout changes such as rotation and varying status bar heights. This session will cover the basic concepts, IB support, and API you'll need to get started using auto layout.

Welcome to session 202, introduction to Auto Layout for iOS and OS X I'm Marian Goldeen, I'm an engineer on UIKit and I'm here to help you get acquainted with AutoLayout, so come on in, the water's fine Well, what is Auto Layout? Some of you probably remember that it was introduced in Cocoa last year, and it's new this year on iOS.

But just to make sure everyone knows what we're talking about, when we say Auto Layout we're not talking about the layout of autos at a parking lot.

We're talking about the layout of your controls and views in your user interface.

Auto Layout is a constraint-based, descriptive layout system Well that's very well and good, but what do I actually mean by that?

Hard-coded layout, which you're all familiar with, is when you say put some specific magic numbers in for your frames, like for instance the button's frame origin is 124, 396.

And that works just fine as long as nothing happens. Of course, something changed a little bit and then that frame origin doesn't actually do what you want.

So, hard coded layout is not descriptive and it's not what we want.

Auto Layout is descriptive, because instead of just putting out these numbers that have no actual meaning, we describe what we want from our layout, for example that we want the buttons centered horizontally in a superview and we want it a fixed distance from the bottom.

And if you think about that description, those two aspects that we're describing can be represented as linear equations.

The button's centerX is equal to the superview's centerX.

The button's bottom is equal to the superview's bottom, minus the padding.

And those equations are what we call constraints.

So that's the constraints-based part of it.

So, if you describe the layout, because it's descriptive, with constraints and here comes the auto part the system calculates the frames for you automatically.

Here's what we're going to go through today: We're going to start with just setting up a simple constraints-based layout.

Just where do you begin?

Then I'm going to tell you a little bit about what's going on behind the scenes to take care of that layout.

After that we're going to discuss the visual format language which is some helpful things in code to make your code easier to read and actually a bit more concise.

I'm also going to tell you about a couple of pitfalls that are important, and unfortunately you're all going to hit them as you get started so it is good to know about them ahead of time.

And there's an important incompatibility issue that I want to point out.

So let's get going and the best way to start with setting up auto-layout is with a demo.

So here's what we have is the single-view template from iOS and we're ready to go.

The utility panel on the right, tab on the left we have the file inspector, and if you select the file inspector down in the middle, there's a checkbox that says "use auto-layout".

And this checkbox is now could be checked by default, but I actually want to start out this demo by unchecking it, just to give you a sort of where we are coming from kind of demo.

So suppose you pull a button now, off your palette and drop it in.

As you move around, Xcode draw this guide for you, and what this guide is telling you when you're not using auto-layout are different from what they are telling you when you are using auto-layout.

So what are they telling me right now is, if I choose this frame, I'm going to be in position as a standard position from this side and if I choose this frame, my frame will be centered horizontally as a standard distance from the bottom.

So that's like my picture before so I'm going to choose a name and I build and run.

And I've done here is I've done hardcoded layout I've just set the frame of the button although I might not know exactly what frame I set that's all I did.

And when I rotate, of course the button's offscreen, that's not what I want it.

But if we've done exact the same thing with auto-layout just remove this to start from the begining Turn on auto-layout, and if we do the exact same thing with auto-layout, once again we have this guide, but this time the guide is indicating something different.

The guide is saying, well, if you put it here, you're telling me that I want a constraint to have the left edge and the bottom edge and the button of this fixed each time I define the distance.

And if you put it here, you're saying - I want this button centered and I want this at a distance from the bottom.

So that's an important difference from saying, I want this frame, that happens to be now centered, I want this always to be centered.

So when you drop it you're telling IB what constraint you want and IB obediently create some.

You can see this little blue line stay. You can roll over them and kind of see them better.

The vertical, that's the centering one. And there's a little eye being the spacer.

If you look over here on the left, you see that the constraints are full-fledged objects in IB, you can connect up outlets to then and we'll be doing that later.

Also once they're selected they have attributes which we'll talk about in a later demo And if I build and run at this point, then I've described my layout and the layout system calculates the frame for me, and it gets them right.

So that's how you describe the layouts in interface builder.

You can also do them in code. Now interface builder did a couple of steps for you, and so you're going to have to do the two steps separately and those steps are creating the constraints and adding them to the view So step one, you create the constraints. And the API for that is in NSLayoutConstraint.h. This is a header which is exported by both AppKit and UIKit.

You don't have to #import it separately, it's in the umbrella headers, but I'm telling you the header name in case you like to read header comments.

But the general form of a constraint is this equation, and that helps you remember the API. But the attribute of the first item is equal to a multiplier times the attribute of the second item plus a constant.

And actually, simplifying things a little bit, that equals sign can also be an inequality.

It's easier to look at the equals sign for now.

So, the API follows directly from that. It's a little bit lengthy when you first look at it, but you keep your head on and the first item is on the left side of the equation there for the first two arguments.

The attribute can be center, left, top ... then the relation is the equals part or the inequality, then the second item and its attribute are the fourth and fifth arguments, and then come the multiplier and the constant Concretely, with the example we're using so far, you just have the center of the button is equal to the superview center, and so you see that our relation is NSLayoutRelationEquals...

you see that our attributes are NSLayoutAttributeCenterX, multiplier one constant is zero, bottom same deal, we set the attributes are bottom relation is still equal, we have minus as padding, you have to remember your sign here, if you get that wrong it does the other way.

A this point you may be looking at the API and thinking "that's kinda a lot of code, it was pretty easy to do an interface builder, why would I want to do it code?"

There's a few reasons One reason is didactic, which is you're trying to teach yourself what's going on And you can remember OK there's a unitary equation, there's our elementary API and I can kinda put it together Now I understand what IB is doing for me, that's why I'm doing it here But of course there are other reasons for using the code And sometimes you just kinda got to. Like you have a ViewController that has different views at runtime, and those views you want to replace and so you have to set up the constraints at runtime.

And there are a few kinds of constraints that you can't set up in interface builder and you must set them up in code So there's some reasons why you actually care about the code So, now that we've created our constraints in code, the next thing we want to do is add them to a view.

And that's another piece of API that on AppKit is in NSLayoutConstraint.h. In UIKit we put it in UIView.h.

It's defined in a category on NSView in AppKit and UIView in UIKit.

It's just addConstraints - simple enought There's also addConstraints, removeConstraint, removeConstraints. They all go together.

Which you might expect So OK, you need to add your constraints to a view - add them to which view?

The answer is you want to add them to the two views closest common ancestor where every view is considered an ancestor it itself.

So what I mean by that is: if the two views are siblings, you add the constraints to their parent.

If the two views are cousins, you add the constraints to grandparent.

If the two views are parent and child, which they are in this eample which we're using, you add the constraint to the parent.

SO now I'm just going to run through that same example using code So I'm just going to do everything I did in IB,in code... remove the button So I'm going to do it in code. Go to my ViewController, doing this in -viewDidLoad, a perfectly good place to set up your constraints It doesn't have to go there, but it's a good place to do it sometimes.

And I'm gonna just #define in the code that I wrote already.

Alright, here I'm jst setting up my views, creating my button and adding it as a subview So here's where I create my constraint, and it's exactly like I had in that slide We have the button's center-x is equal to the superview's center-x times 1 plus 0.

And we add the constraints to the superview, because it's the parent of the superview and the button I should say the ancestor And then the second constraint we have the button's bottom is equal to the superview's bottom times 1 minus 20 as the constraint.

Build and run And it works just like I set it out in IB.

There's something I want to point out to you here.

And what I want to point is that if I search for setFrame - not found!

This is important! I never set the frame of that button because the layout system is what's setting the frame.

And that's something you need to keep in mind when using autolayout, you're not the one who's setting the frame, you're just describing the layout Now some of you who might be fairly experienced in doing layout may have had this thought cross their mind: I can do that with spring and struts!

Everything I did here I could do with springs and struts, just flexible left margin, flexible right margin, flexible top margin and the bottom.

It gets the same behaviour - you're absolutely right.

Everything you can do with springs and struts, you can do with constraints.

This turns out to be important But with constraints you can do an awful lot more.

Constraints can apply to any two views, regardless of their view hierarchy.

Then remember with springs and struts you need to have that parent-child relationship, and furthermore it's directional from the parent to the child and the position and size of the child doesn't affect the parent at all.

Constraints can go either way You can establish maximums and minimums of constraints, and remember you can use inequalities.

And constraints can also be prioritized, so up to now the constraints we've been talking about have been required.

There's a property on NSLayoutConstraint called priority... the datatype is different in AppKit and UIKit WIth the prefix, and we didn't just do that because of the prefixes, we actually have slightly different system-defined priority levels for the two kits just because it's a little bit of a different situation. So they got different names The most important thing to remember right now, and what you need for going forward is that the required priority level is 1000 Anything less than that is not required and will be satisfied by the system as much as possible So any error will be minimized.

And you can set those priorities on a constraint any time before the constraint is added to a View So with that in mind, I can continue the demo and do a few things that you can't do with springs and struts So here's where we left off at the end of the first demo We have our button and we had a centering constraint and bottom spacer constraint.

And we decide this button actually needs a label to go along with it So notice I'm getting another guide here to the right of the label, and that guide is letting me know that I'm going to ask for a spacer constraint between the label and the button That is what I want, so I'm going to stop there And you can actually, I'll just show it to you here, cos it's kinda small...

Apple, Inc. AAPL
1 Infinite Loop Cupertino CA 95014 US