Essentials • iOS, OS X • 54:12
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.
Speaker: Marian Goldeen
Unlisted on Apple Developer site
Downloads from Apple
Transcript
This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.
Welcome to session 202, introduction to Auto Layout for iOS and OS X. I'm Marian Goldeen. I'm an engineer on UI kit, and I'm here to help you get acquainted with Auto Layout. 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 in 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 changes 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, we want the button centered horizontally in its super view, 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 center X is equal to the super view center X.
The button's bottom is equal to the super view's bottom minus the padding. And those equations are what we call constraints. So that's the constraint-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 setting up a simple constraint-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's good to know about them ahead of time. And there's an important compatibility 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 what we have is the single view template from iOS, and we're ready to go. In the Utilities panel on the right, in the tab on the left, we have the File Inspector. And if you select the file inspector and down in the middle there's a check box that says use Auto Layout. Yeah, there it is. This checkbox is now going to 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're coming from kind of demo. So suppose you pull a button out.
of your palette and drop it in. As you move it around, Xcode draws these guides for you. And what these guides are telling you when you're not using Auto Layout are different from what they're telling you when you are using Auto Layout. So what they're telling me right now is that if I choose this frame, I'm going to be in a position that's the standard distances from the sides. If I choose this frame, my frame will be centered horizontally and a standard distance from the bottom. So that's like my pictures before, so that's so I'm going to choose, give it a name.
And I build and run. And I find that what I've done here is I've done hard coded 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 off screen. Darn it. That's not what I wanted.
But if we'd done the exact same thing with Auto Layout, Let's just remove this and start from the beginning. Turn on Auto Layout. Now, if I do the exact same thing with Auto Layout, once again, I have these guides, but this time, the guides are indicating something different.
The guides are saying, well, if you put it here, you're telling me that I want constraints to have the left edge and bottom edge of the button at this fixed H-I-defined distance, and if you put it here, you're saying, I want this button centered, and I want it a distance from the bottom. So that's an important difference from saying, I want this frame that happens to right now be centered. You're saying, I want this always to be centered.
So when you drop it, You're telling IB what constraints you want, and IB obediently creates them. You can see these little blue lines. You can roll over them and kind of see them a little better. There's the vertical, that's the centering one, and then there's a little I-beam for 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 them, and we'll be doing that later. Also, once they're selected, they have attributes, which we'll also 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 frames for me, and it gets them right. So, thank you.
That's that simple demo. So that's how you describe the layout with constraints in Interface Builder. You can also do it 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 a view.
So step one, you create your constraints. And the API for that is in NSLayoutConstraint.h. This is a header that's exported both by AppKit and UIKit. You don't have to pound 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.
The general form of a constraint is just 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 I'm actually simplifying things a little bit. That equal sign can also be an inequality, but it's just easier to look at the equal sign for now.
So the API just follows out 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, bottom, there's a bunch.
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 that we're using so far, you just have the center of the button is equal to the super view center. And so you see that our relation is NS layout relation equals. You see that our attributes are NS layout attribute center X. Multiplier is one, constant is zero. Bottom, same deal, we accept our attribute is bottom. Our relation is still equal. We have minus our padding.
You sort of have to remember your sign here. If you get that wrong, it goes the other way. And you say, "Oh, whoops, got my sign wrong." At this point, you might be looking at this API and thinking, that's kind of a lot of code. It was pretty easy to do in Interface Builder.
Why would I ever want to do it in 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, okay, there's this unitary equation, and that's our elementary API, and I can kind of put it together. And 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 kind of got to. Like you have a view controller that vends different views at runtime and those views you want to replace, so you have to set up the constraints at runtime. And there are a few kinds of constraints that you actually can't set up in interface builder, and you must set them up in code. So those are some reasons why you actually care about the code.
So now that we've created our constraints and 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've put it in UIView.h. It's defined in a category on NSView and AppKit and UIView and UIKit. And it's just add constraint. Simple enough. There's also add constraints, remove constraint, remove constraints. They all go together, and then you're doing what you might expect. So, okay, you need to add your constraint 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 of itself. So what I mean by that is if the two views are siblings, you add the constraint to their parents. If the two views are cousins, you add the constraint to their grandparent. If the two views are parent and child, which they are in this example that we're using, you add the constraint to the parent. So now I'm just going to run through that same example using code.
I'm just going to do everything I did in an IB in code. Remove the button, so I'm going to do it in code. Go to my view controller.m file. Doing this in view did load in the view controller, 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 going to just pound define in the code that I wrote already. Don't need this view. Let's give us a little more room. All right. Here I'm just setting up my views and my button, 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 center X is equal to the super view center X times 1 plus 0. And we add the constraint to the super view because it's the parent of the super view and the button.
I should say the ancestor. And then the second constraint we have the button's bottom is equal to the super view's bottom. Times 1 minus 20, add the constraint, build and run, and works just like I set it up in IB. There's something I want to point out to you here.
And what I want to point out is that if I search for set frame, 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 you're using Auto Layout. You're not the one who's setting the frame. You're just describing the layout.
Some of you who might be fairly experienced with doing layout may have had this thought cross their mind. I can do that with springs and struts. Everything I did here, I could do with springs and struts. Just flexible left margin, flexible right margin, flexible top margin on the bottom. I get the same behavior. You'd be absolutely right. Everything you can do with springs and struts, you can do with constraints.
This turns out to be important. Marian With constraints, you can do a whole lot more. Constraints can apply to any two views, regardless of their view hierarchy. You may remember with springs and struts, you have to have that parent-child relationship, and furthermore, it's directional. It goes from the parent to the child, and the position or size of the child doesn't affect the parent at all. Constraints can go either way.
You can establish maximums and minimums with constraints. You remember that you can use inequalities. This comes in handy. And constraints can also be prioritized. So up to now, the constraints that we've been talking about have been required. There's a property on NSLayoutConstraint called priority. The data type is different in AppKit and UIKit just 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 cases. So we've 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 1,000. Anything less than that is not required and will be satisfied by the systems as much as possible. So any error will be minimized. And you can set the priority on a constraint any time before the constraint's 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 had our button, and we had a centering constraint and a bottom spacer constraint. And we decide that the 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 space or 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 because it's kind of small. We have a horizontal spacer there between the label and the button. And I'm going to give the label a sort of slightly better name.
A nice little thing you can do in Interface Builder is if you select the view controller, In the attributes for the view controller, you can change the simulated orientation. And it'll actually lay out as if you had built and run. And so, indeed, here we have the label is actually just sticking right there next to the button. It didn't move around or anything.
We've constrained it to be just this fixed space to the left of the button, and the button's centered, and it's all working the way we want. So the next thing that might happen is you might have a different label for the button. You might have a label that takes up a little more room. I want to make sure I'm still centered and got all the constraints that I want.
So now I have the button centered, it has a long title. Let's say I did build and run at this point. Well, I'm running into a little problem here with my layout. I like the landscape layout, but in the portrait, the label has kind of fallen off the side of the view. It's actually clipped there. You know, the view is just kind of out of bounds. And I don't want that. I want that label to never get closer to the edge than the HIDefied standard margin.
And, but you know, I want to allow it to get bigger than that in landscape. And so that's where an inequality constraint could be helpful. So I want to put in a constraint on the left as a left spacer for the label. So I want to create a new constraint, and there's two places you can do it. You can find it in the editor menu under Pin.
I'm going to do it from this button down here in the lower right. There's a little picture like an I-beam, and that's your hint that it's a constraint. So we'll pop it up, and I want to pin the leading space to the super view, and since English is left to right, leading space gives me the left one.
And then if I look over on the right in the attributes inspector, I don't want the relation to be equal, remember? I wanted it to be a minimum, so I want greater than or equal to. And of course, 79 is too big. I just want the standard space. So I click that for standard, and now we should be good and not have the label bumping into the edge of the frame.
Hmm. All right. Still good in landscape. But we've clipped the label. So it turns out that it's more important to the constraint system at this point to keep that button centered than it is to avoid keeping the label. But I say, hey, look, there's kind of more room there on the right. I'd actually like it to be more important to show the entire contents of the label than it is to keep that button centered. So this is where I want to change the priority of my centering constraint.
So I stop and I go back. I can either select the button, which will show the constraints, and then I can select the constraint. Or I could have gone straight to the constraint in the menu on the outline view on the left and selected it there. But I select the constraint, and then I have these priorities. So you see that by default it had been brought out with a required priority of 1,000.
And once you start moving this slider, you get a helpful tool tip that gives you an idea of where your constraint falls in priority with respect to system-defined priorities. And I actually want a pretty low priority here to be lower. It's not that important to the system to avoid clipping the label. So I make that priority pretty low on that centering constraint to be lower than that. Build and run.
Okay, now I've got what I want. I can read everything. I don't have any label truncation. I rotate, the button is centered in landscape where there's room to do that. I go back to portrait, and the button isn't centered, but it's almost centered. It very clearly did the best it could, so I'm satisfied.
So now I've showed you the priorities and the inequalities and how they can actually work together to make your layout do what you want. But I want to show you one more thing, which is about cross-view hierarchy constraints. It's not that unusual to maybe have some sort of container view containing some portion of your UI. So I'm going to make a container view.
And I'll just make it be Aqua guidelines. Okay? And, you know, as I was pulling it out to those guidelines, I was telling it where I wanted my constraints. I'm perfectly satisfied with these margin spacer constraints. Normally, with a container view, you might make the background color completely clear. I'm going to leave it showing just so you can follow my demo a little better.
So I'll leave it showing. Now, suppose that I've written some appearance containment stuff with appearance when contained in. So I just happen to have a view subclass here, which makes switches orange. So this is going to be a container view that makes switches orange. So I'll pull a switch out in there.
The constraints that IB will make for me for this switch are with respect to its super view. So I'll just go ahead and set those in, and it's centering in its super view. It looks pretty good in landscape because I do want the switch centered over the button. I'm going to build and run.
And the switch is orange, which it got from the appearance containment. But it looks a little cockeyed. It looks pretty good in landscape where it's centered over the button. But in portrait, it's still centered in its super view when I actually want it to stay centered over the button, but of course we can't count on the button to stay centered. So I want to tie the center of the switch to the center of the button.
Marian Goldeen And I think, oh, you know, I ought to be able to do that in IB. Maybe I can do that in IB. I could go into IB and I could select the two items and add a constraint between them, too. So you go, okay, so I'll select the button, and then I'll shift select the switch. Huh, right, it selected its super view because it's at the same hierarchy level as the button. And you go, oh, well, IB can't quite do this yet. So that's where you say, well, I better set this constraint up in code.
Marian Goldeen Remember that the constraints are full-fledged objects in Interface Builder so that you can hook up the outlets. So we go to the editor view so we can look at the header at the same time as we're looking at the storyboard. And I've already set up my outlets to save us a little time. We'll need to remove the centering constraint on the switch because we're going to be putting our own centering constraint on. So we'll need an outlet to that in order to remove it.
And when we send remove constraint, we'll have to be able to send it to the view that has the constraint, and that would be the common ancestor of the switch and the container view, which is the container view. So we set up that outlet. And then we need outlets to the two controls that are going into the constraint that we're going to create. So we need an outlet to the button and an outlet to the switch.
Okay, once that code is set up, I mean, once those outlets are set up, excuse me, Then we can go to our code in viewcontroller.m. Again, I'm doing this in viewDidLoad. And the first thing we do is we remove the constraint we don't want. So it's simple enough.
We send the old constraint in the message remove constraint to the view that has it, which is the switch container. And then we create The constraint we actually want, which is that the center of the orange switch is equal to the center of the button. And again, the multiplier is 1 and the constant is 0. And we add the constraint.
And sure enough, now the switch stays with its center over the button, which is what we wanted. Rotate, and we're good. So there we have an example.
[Transcript missing]
And it was a pretty simple example, but you might have already begun to notice that something's going on behind the scenes.
There's something global going on. You know, some constraint or something might apply to some view over here, but because of the situation, another view somewhere else in the view hierarchy might get affected, and so there is something going on behind the scenes. When the UI's just sitting there, it's drawn, everything's fine, then something happens. Say, for example, you rotate the device or there's a window resize, and the situation has changed, and so the solution for the constraints, the answer, the frames, has changed. So we need to redraw, and before we redraw, we have to apply the new layout.
Other things that might happen is a view might get added, such as a badge of some kind, and when a view gets added, it's going to be added with its constraints, and they have to be brought into the system, and you get a new view, and you get a new solution, and that has to be applied, so we're gonna have to redraw.
[Transcript missing]
You can poke the system and say, you know, hey, I'm sending in a request for any of these passes with set needs display, set needs layout, set needs update constraints. The only difference between the two kits is the argument, the colon on the app kit side, and the only reason there's this difference is for internal consistency within the kits.
The only really new piece of API is set needs update constraints on UIView. UIView already has set needs-display and set needs-layout from the existing display passes, and they just are -- we co-opted them with the Auto Layout, so when you're using auto-layout, the same ones work. And set needs-update constraints completes the deal. You can also have this sort of thing happen immediately rather than waiting for the next time the system is going to do it, with layout if needed on UIView and UI window, and layout subtree if needed on NSView and layout if needed on NSWindow.
This reminds me of something I meant to say earlier, which is with the cross-view hierarchy constraints, this question doesn't come up in AppKit, but in UIKit, UIWindow is a subclass of UIView. I don't mean cross-window constraints, okay? We don't support that. Your constraints all have to be within the same window to apply. So in case -- that's a subtlety, but just, you know, remember that.
Anyways, let's go back to these phases of display one more time. We're interested in layout. So let's just say that we know that the constraints are up to date. And we know that after we've laid out, we're going to draw somehow. So we're not going to think about that.
What's happening during this phase where we apply the layout? That's where the system is sending layout on AppKit and layout subviews on UIKit to the views. Now, layout subviews heretofore has been a no-op on UIView. And going forward, if you're using Auto Layout, layout subviews in UIKit is where the Auto Layout solution is applied.
During that layout pass, we take our solution and we send set frame in NSView and set bounds in set center. to UI view to get the solution onto your view hierarchy. So unfortunately when you're working with layout, sometimes you run it and you don't see what you want, and you say something's gone wrong with the layout. Well, if you're using Auto Layout, you need to think again because the layout task is pretty simple. We're just applying these frames from the solution.
Something might have gone wrong with your constraints. That's more likely. There are two things that are required about your constraints. First of all, the constraints that you set must be sufficient to fully define the position and the size of your view. Also, the constraints must not conflict with each other. You must actually have something that could exist in real life without an alternate dimension.
I think I pulled a fast one on you because I said the constraints have to be sufficient. What's going on here? I said I have the constraint saying that the button's center X is equal to the superview's center X. And I have a constraint saying that the button's bottom is equal to the superview's bottom minus the padding.
But did I put any constraints on that had anything to do with the size of the button? How -- where did the size come from? I didn't put any constraints on there. Well, it came from here. There's a method on NS view and UI view called intrinsic content size.
And the base implementation basically says, oh, nothing, you know. But for some -- for some views, there actually is a size that makes sense that we could return. Like for a button, for example, you know about the content, like the label or the image. You know something about the size of it.
You know about the image that's going to be used for the background of the button. So the system can actually come up with a preferred size. So the -- the kits have implemented intrinsic content size to return actual sizes in -- for specific subclasses of the views. And you can also override this, too.
I'm not going to talk any more about this because there's a lot more details and in the session on Thursday, mastering Auto Layout, you can learn a lot more. But now you know I'm not just tricking you. Okay. All right. So what I'd like to dive right into now is the things that can go wrong.
However, Interface Builder does its very best to help you avoid these problems. That means it would be hard for me to demonstrate these problems in Interface Builder. So I'm going to demonstrate these problems in code. And that elementary API, while it's easy to remember, once you start writing out four of those for every view, You get kind of a big pile of hard to read code. So that's why I'm going to first talk about the visual format language, which makes the code easier to read, because then you'll follow along better when I show you the things that can go wrong.
So here's an example. Here we have a pretty simple constraint that we're setting up, a constraint between this cancel and accept button. And when you look at the picture, you can see right away that you're setting up a fixed spacer, and that the cancel button's on the left and the accept button's on the right.
So when you look at the code, if you look at it fast, you might even get left and right mixed up because that left attribute is right there next to accept button, and we're not saying the accept button is on the left, we're saying that the left side of the accept button is to the right of the, and okay, you know, so the, that means the accept button is on the right. It's just a lot easier to look at this picture. You don't even hardly have to think about it.
So wouldn't it be awesome if you could just say, here are my pictures, you please write the constraints for me. If there was only, there was a way to draw such a simple picture in Xcode. And for those of you who like to do ASCII art, there is a way.
[Transcript missing]
The API is constraints with visual format. And the parser will just read that ASCII format string and write those elementary API constraints. There's a couple of other arguments. We'll get into that. And there's the views dictionary which tells the parser what objects those strings represent. And we even have a nifty little macro for you so that if those strings are the same as your local variable names, you can use NSDictionary of variable bindings to create that dictionary which makes it a lot easier. I like that a lot.
If you break in the debugger and look at your views dictionary, it'll just be what you expect, except without bogus addresses. Naturally, you want to express more than just two views are next to each other, and taking automatic sizing, you might want to be able to put in specific sizes. So you can. You can specifically say what sizes you want. And you can do even more. I'm going to give you a few simple examples to give you an idea. There's lots more information in the documentation, lots more examples.
It's actually pretty straightforward. You can get the hang of it pretty quickly and then refer to the documentation to refresh your memory. So for example, you can put in inequalities next to those numbers. And so here's a view that has a minimum width of 60. So it has a width that can be greater than or equal to 60. And we actually have a priority on that. It's at 700. So width greater than or equal to 60 at a priority of 700.
Of course, we want to do vertical constraints too, so we turn our head on our side to look at the ASCII art and put a V colon in front of the string. And then we know that we're vertical. We have a little shorthand so you don't have to put in zeros if you want the views right next to each other. You just abut the brackets. And you can also say that the dimension of one view is equal to the dimension of another view. In this case, it's the height. I'm saying that the yellow box height is equal to the red box height.
: You can combine a bunch of views all at once, and you can talk to the super view. The pipes there on either side are saying, "Connect me to the super view." I put in an H colon here. That's the default, so if you don't put in that prefix, it's going to be horizontal, but sometimes it's nice to put in the prefix to just keep your mind straight.
Now I want you to think about this example for a minute. We actually have a bunch of constraints here. Remember the API is constraints with visual format. We've got four spacer constraints plus a width constraint. So if you think about that, that's just right there. That's five of those elementary API messages that were all kind of hard to read.
And you're pretty happy to do that if you add on to that an option, an alignment option, NS layout format align all baseline. You've added a few more constraints to keep them lined up vertically. So you're getting a lot of bang for the buck with this syntax. And now that I've introduced this syntax to you, now you can follow along with the things that can go wrong.
What were those things again? All right. You have to make sure that you're not going to have a lot of constraints. You have to make sure that the constraints are sufficient to define the position and size of the views. And if you don't have that, if you break that, you have ambiguous layout. Also, you must make sure that the constraints do not conflict with each other. And if you break that rule, you have unsatisfiable constraints. So that's what I'm going to demonstrate to you next, ambiguous layout and unsatisfiable constraints.
All right. So here we are. Back in our view. And now let's say I want to add to that container view a couple of buttons up at the top. A cancel button and an accept button, how about. All right? So I have some code to add it in View Did Load to create my buttons and add them as subviews.
And then I say, okay, I'm going to use that visual format language, because it's cool and it will save me a lot of typing. Plus when I come back later, I'll be able to read what I did. And the views I'm interested in are the cancel button and the accept button. So I'm going to use the NSDictionary of variable bindings macro to set up my views dictionary. And then I get my constraints.
with constraints with visual format. And I remember that the super view of the buttons is that switch the container, and this is why I left the container where you could see it. It actually already has the margins from the view controller view. So I don't want to double up the margins.
So I have the cancel button flush with the border of its super view. And same thing with the accept button on the right. Then I decide I want the system-defined distance between the two of them. And of course I want them aligned all on the baseline. So I'm like, okay, that's good. I've defined my constraints. So I build and run.
I get something that doesn't look that good. It kind of looks wrong. The accept buttons have like floated up to the top of the window. And it's definitely not what I had in mind. I mean, after all, there's subviews of this container view and ugh. At this point, this is where you need to start thinking, do I have ambiguous layout? And this is such a simple example that if you think about it, you might actually just be able to solve this by thinking, but we're going to pretend that you can't.
Topkit has some great visualizing aids for this that you'll hear more about in mastering Auto Layout. I'm not going to go into them here. Unfortunately, not available at this time on iOS. However, we do have something you can do in the debugger. It's rudimentary, but it gets the job done. You can pause in the debugger. And you can send to the key window.
Message under Auto Layout trace. All right, so you send this message, you get a message You get an abbreviated view hierarchy. And look at this. Ambiguous layout here. My rounded rec button. Another ambiguous layout. All right. So it was as I suspected. I have ambiguous layout. And the reason the layout is ambiguous is because I didn't say exactly where vertically I wanted those things, you know? I just said I want them aligned with each other vertically, but that's not quite enough to get an answer. So... I go back to my code and I say, all right, I need to vertically align something. So let's just vertically specifically align the cancel button.
And I only have to align the cancel button, because remember, the accept button is already aligned with the cancel button vertically with the align all baseline. There we go. It's better. So it's enough to align the cancel button. I just say I want the top. Remember, turn your head on the side.
This is vertical. This is vertical. So that's the top. And I'm just using the same views dictionary. Don't worry if the views dictionary has a few extra views in it. You're fine just using the same one. So once I do that, these buttons should have a position vertically.
So I build and run, and sure enough, I'm good. There it's set up where I want vertically. But I don't actually want to walk away from this layout with the cancel button all bigger than the accept button. And unfortunately, I wasn't able to quite demonstrate this to you, but what could have happened here is we could have the not always being the same one that's bigger.
That's what happens with ambiguous layout is you get sort of unpredictable results. Sometimes it will be always wrong in the same way and then you're like, oh my God, and then I ran it and it was wrong in a different way. Okay, that's a big cue that you've got ambiguous layout. And the reason we have -- in fact, well, I'll just show you in the debugger real quick, too.
Sure enough, layout's still ambiguous. And the reason we have ambiguous layout is because the system couldn't quite satisfy having the boundaries of those buttons pinned to the edge and having the fixed space be the Aqua distance and fitting the button with its intrinsic content size that we mentioned.
So it had to stretch one of the buttons, but it couldn't really pick which one to stretch. You're like, well, but I don't want you to stretch it in this situation. Obviously, you should just be splitting the difference. Marian Goldeen Well, yeah, but it's not so obvious, so you actually have to do that yourself. Marian Goldeen All right.
So the way we do that, a nice, easy-- there are many ways, but the way I'm going to do it is I'm going to say, well, actually, I want the accept button width to be equal to the cancel button width. And I'm going to spell cancel button correctly, or else I'm going to demonstrate something else. All right, no, I think I spelled it correctly. And yes, so this is much better That's a lot more kind of an acceptable sort of layout behavior. And if I pause and look at the Auto Layout trace, We're good. We're no longer ambiguous.
So, yeah. So the problems are a little subtle when you have ambiguous layouts, which is why I spent a little more time on this. Because when you have unsatisfiable constraints, the system actually kind of squawks at you. So I'll just stick in an unsatisfiable constraint here real quick to show you that.
So one good way that you end up with unsatisfiable constraints is you kind of forgot what you did and you come back later and you say, oh, I want this other constraint, okay? And so let's say I forgot what I did and I want the cancel button to be 130 points wide, so I just put in this hard-coded width on it, and at this point I build and run.
and you may have noticed a complaint right away in the logs. One of the constraints that we had established before, which was required, it was required but it's broken and that's that distance between the two buttons. So down here, if I had my breakpoints on, I would have hit a breakpoint too.
So, You get a big debugger spew, and don't be afraid of it. Go to mastering Auto Layout, and you'll learn about reading through that spew. But the important thing here is this. I have created unsatisfiable constraints. So that's what's going to happen to you if you do that. And I'll just remove that because it's ungainly. And then we're back to the world that we like. All right. Okay. And I want that button. All right.
So now that I've pointed out the things that can go wrong, you're pretty much ready to go, except for one thing. There's some compatibility issue that I need to make sure you all know about. Everybody. What is it you need to know about compatibility? You're ready to convert to Auto Layout. What do you care? Well, first of all, great.
I'm glad you're ready to convert to Auto Layout. Please attend both sessions on Thursday, Mastering Auto Layout and Auto Layout by Example. Lots of great help for doing the conversion. However, you will have to know about this compatibility issue because it will affect something you do when you create your constraints.
Well, maybe you're not ready to convert. You're not going to convert yet. You're just planning on it. Well, you'll be glad to know that none of this system turns on until you, in your app, add constraints and kick it off. But you still might want to continue and get to know this thing.
And what if you just want to use Auto Layout in part of your application, or you have a new section that you can want to use Auto Layout there, and you don't want to have to worry about it elsewhere? Well, this is what the compatibility is for. The compatibility is for being able to use Auto Layout in part of your application, and that's what you need to know about.
So, let's say you have some general UI layout where you have a large view that has a flexible width and flexible height auto-resizing mask, and then you have on the left a tall, narrow view that has a flexible height, and you have a little view in the upper left that isn't flexible at all. And into this happy world, you introduce some views that you're going to layout with constraints. Well, yep, the Auto Layout system now gets turned on.
So what happens to all those views that you didn't apply constraints to? Well, I said that every view has to have constraints sufficient to, you know, define its position and size, don't we have ambiguous layout? And the answer is no. We have a property called translates auto-resizing mask into constraints. And this property is on NSVU and UI view.
This property is on by default. So you add constraints, Auto Layout gets turned on, and all your views which don't have constraints now get system defined constraints to keep them behaving as if they had their old auto-resizing mask. Remember, everything you can do with springs and struts you can do with constraints, so this is how we can do this. There's one more subtlety. You might have noticed that two of these views have auto-resizing masks, and But one of them doesn't.
What happened to that view, right? Well, the truth is, even if your view has auto resizing mask none, none doesn't really mean none. There is actually an implicit auto resizing mask, which is fixed left margin, fixed width,
[Transcript missing]
This is where it comes in. Those views that you're setting up with constraints, they have to turn this off.
Because, you know, if you're trying to position it with constraints, you don't want system auto resizing mass constraints getting in there and fighting with your constraints. So you have to turn this off unless, of course, you set up your constraints interface builder, because interface builder takes care of making sure this property is correct. And, you know, if you were quick and paying attention, because I did, you know, try to slide it past you, in my code examples, all those views that I set up in constraints, I did send this message to to make sure that the demos would work.
All right, so taken together, this should be enough to get you going to start playing around with Auto Layout. I showed you how to set up constraint based layout, both in IB and in code. I told you -- gave you an idea of what's going on behind the scenes in layout.
I showed you the visual format language so you can set up your constraints in a more readable fashion in code. We talked about two important things that can go wrong. And we talked about compatibility. So in summary, remember you have the elementary API. This is great for when you're learning what's happening.
And, you know, also when you need it, we have the visual format language. and we have interface builder which gives you the most readable situation that you can possibly have and it's incredibly useful. So don't forget that interface builder gives you some power. Our related sessions. Be sure to attend the other Auto Layout sessions.
Best Practices for Mastering Auto Layout and Auto Layout by Example. Those are great. They'll really fill in a lot of details that I left out. Also, for those of you on iOS, the evolution of view controllers on iOS, he has some examples of some specific view controller related Auto Layout that I didn't mention here.
For more information, please contact Jake Behrens, our frameworks evangelist. The docs are terrific. Don't forget to refer to them when you forget what I said. Last year's keynote on Cocoa Auto Layout is largely still applicable on iOS as well. If you're interested in how we actually arrive at the solution given these constraints, you might be interested in the arithmetic constraint solving algorithm that's published at the University of Washington CS website. If you like linear algebra, I highly recommend this paper. And following along with paper and pencil and working out the examples, it's pretty interesting. And of course, there's the Apple developer forums.