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

Configure player

Close

WWDC Index does not host video files

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

URL pattern

preview

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

$id
ID of session: wwdc2011-103
$eventId
ID of event: wwdc2011
$eventContentId
ID of session without event part: 103
$eventShortId
Shortened ID of event: wwdc11
$year
Year of session: 2011
$extension
Extension of original filename: m4v
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: [2011] [Session 103] Cocoa Autol...

WWDC11 • Session 103

Cocoa Autolayout

App Frameworks • OS X • 59:16

In Lion, we revisit the basics of how views are placed in windows for the first time since Mac OS X 10.0. Springs and struts (i.e. autoresizing masks) are out, dashed blue Interface Builder guides are in. Rather than being one-off helpers for setting a frame in IB, guides become persistent objects that maintain a relationship at runtime. Come see what's going on, and what merited such a big change.

Speakers: Kevin Cathey, Ken Ferry

Unlisted on Apple Developer site

Downloads from Apple

HD Video (1.27 GB)

Transcript

This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.

Good morning. Welcome to session 103, Cocoa Autolayout. My name is Ken Ferrry. I'm an engineer in the Cocoa Frameworks group. So today we're talking about layout. Layout means different things, different contexts. So what do we mean for today? We're talking about the basics of how you get content into a window where the user can see them. Every graphical environment has some mechanism for doing this. In HTML, you have blocks, and you're trying -- and you position them with CSS and things like that. In Java, you have components, use layout managers, things like that.

Cocoa has views, and we positioned them through two properties. We have the frame, and we have the autorisizing mask. And collectively, we call this system Springs and Struts, and it's served us well for a long time. It's pretty nice. And this is what we're changing. So I hope we do a good job.

To understand what we're doing, let's call your attention to the image on the left, where we have these blue guides. These are lines that you see in Interface Builder when you're dragging a view around that help you snap it into a really good position, a good initial frame. But they're temporary.

They're gone as soon as you let go of the mouse. They vanish. And what we're changing is that we want to take those guides and invert the relationship between them and the frames. So the guides are actually what we save, and the frames are computed at runtime from the guides.

And we rename the guides because they're persistent. We call them constraints now, because they constrain the way the interface is allowed to change. So in this example, what we see is that no matter what happens in this window, the right side of this button must always be pinned to that space to the right side of the window, and same for the top.

So like I said, I mean, this is a pretty big change. I mean, Springs and Struts has been something we've been using for a really long time, and we're actually happy with it. And furthermore, in other systems, people sometimes aren't very happy with their layout systems. Some of them are very complicated. Very few people will talk about how great Gridbag is. We'll talk about how great Gridbag layout is in Java. So why would we do this? Well, to start to get the idea, let's take a look at the same interface in German.

It's quite a bit bigger in German, as you can see. I probably won't try to pronounce it, because I'll get it wrong. But the thing to realize here is that even though it's larger, what you see drawn here as far as the guides are exactly the same as before.

So the same set of constraints are going to serve to lay it out in English and in German. And what that means is that in order to localize this interface, you need to find somebody who speaks German, but you don't need to find somebody who can also edit a nib file, which is a lot harder than finding somebody who speaks German.

Okay, but we're not doing this for localization. In fact, you might have gotten an impression from some of the things you've seen so far. Localization is in some ways just the easiest thing to show you that's better. But there's a lot. So this is actually pretty important. So we want the work that you do in Interface Builder to go a lot further. Something that you'll find in interfaces today is that when you have a dynamic interface where the content is not known at design time, it's hard to use Interface Builder to do that. You might find it easier to do it in code.

But with layout, because the constraints sort of express things that continue to be true through the lifecycle of the app, you can continue to use Interface Builder and bring the dynamic content in when you have it. When you can't use Interface Builder, we want to make that situation better, too. So when you're doing in-code layout, we think we can make this substantially better. And we have some tricks for this, actually, that I hope you'll enjoy. Design flexibility.

So if the person who's drawing a button, decides, you know what, I need an engraving line, or I need a shadow, or something like that, right now, you have to go through all the code that uses that control and fix it all. And that shouldn't be necessary. That's a bad factoring of responsibility. Localization. We already talked about that, so I'm not going to do it anymore. But we didn't talk about resolution dependence. And this one's maybe the most subtle of the features.

In Interface Builder, with classic Cocoa Layout, if you set the frame and you set the autorisizing mask, there's pretty much no way that frame can be pixel integral at all possible scale factors. And it turns out you really want that. So it turns out that the classic way of doing Cocoa Layout in Interface Builder is sort of incompatible with at least non-integral scale factors with high DPI. So with that, I'd like to get started on the basics of how this works. And for that, I'd like to bring up my colleague, Kevin-- there he is-- Kevin Cathey to show you how this works in Interface Builder.

So as Ken said, my name is Kevin. I'm one of the engineers on the Xcode and Interface Builder teams. I get the privilege of kind of introducing you guys to some of the basic concepts of Autolayout, and I'm going to show you guys how that works by showing it to you in Xcode. So let's get started.

All right, so I've just got a basic Cocoa application here. I haven't done anything, just created a new template. And the first thing I like to do is enable Autolayout for my main menu nib file. And to do that, it's really easy. There's the file inspector in Xcode. I have my nib file open. I can open the file inspector, and there's a checkbox here called Use Autolayout. And I'm just going to go ahead and check that out.

And now my document is using Autolayout. So how does this work? So let me grab a button and drag it up into the upper left corner of my window. You can see I'm getting the guides, but when I let go, I've created two constraints. One here, and one here.

Now, constraints, as Ken mentioned, are relationships that occur between one or two views, and they're relationships that must hold. One way to think about this is we've taken these Interface Builder Aqua guides, and we've frozen them as these relationships. Now, what's interesting about the guides, again, is the fact that they specify some interesting spacing, for example, between two views, or how two views align. And we want to encode that data in our interface and always have it be true.

Now, if I grab my button and drag it to the upper right, you can see I've got different guides, and I've created different constraints. We think that we've made it even easier to create those simple interfaces. There's no longer a need to set an autoresizing mask. As I'm dragging things around, it's doing what I mean, as Max mentioned in the developer tools kickoff yesterday.

So while a lot of things with springs and struts are possible without a layout-- actually, everything that's possible with springs and struts is also possible without a layout-- there's also a lot of things that aren't possible with the auto-resizing mask that are very, very, very simple without a layout. Let me show you a really simple example.

If I grab this button and center it, OK, it stays centered. If I drag out a second button, however, and put it next to it, I've created two constraints. One is a spacing between these two buttons and one to the top. But this button's always centered. So if I run my application-- command-R and resize my window. You can see, sure enough, that button is staying centered, but this button is tagging right along with it. For those of you familiar with the autoresizing mask, you'll know that's a bit tricky. You can't do that only with the autoresizing mask.

So I can preview resizing my window or views by running my application, but I can also preview it really easily in Interface Builder just by resizing my view. And you can see those constraints are taken into account right at design time. So two things I want to point out here.

First of all, I can see the constraints that involve the views in my interface by just selecting them. So if I have this button selected, I can see the constraints for this button, and similarly if I have this one selected. Secondly, constraints are real objects. They appear in your Interface Builder documents just like every other object in IB. So that means I can select them, so I can click on this constraint, and we'll show those two views that are involved in that relationship with the amber highlights.

But I also can see them in the document structure. For example, if I open the jump bar for this content view of the window, oh, there are my two buttons. And there's a new disclosure called constraints that will show me the constraints that involve the views of my subview. So two things to point out there.

So we've talked a lot about taking the Auto Layout guides, the Aqua guides, and turning them into constraints, but it's not just limited to that. We always make sure that your views have constraints. If I select these two buttons and drag them to a spot right here, you can see I still get constraints that make sense since it's in the upper right portion of my window. So what exactly is Xcode doing to help you out? Very simply stated, Xcode is picking the minimum best set of constraints.

So there's lots of possibilities for constraints. If I grab this button, you can see there's lots of possible guides, right? Top, bottom, baseline, center, y. And what Xcode is doing is not magic. We just look at all the constraints that are possible, assign each one a score, and then pick the minimum best set. And we have a simple heuristic that baseline alignments, for example in this case, are more interesting than, let's say, a center or a top.

All right, so we think we've made creating those simple interfaces even simpler, but we've also made creating the advanced interfaces possible. So what I'd like to do now is build just a simple interface with some interesting elements going on with some of the relationships between views that maybe represent some sort of iTunes-like interface with a table view for songs and some buttons for adding and removing things, and maybe sharing with your friends. Legally, that is.

All right, so let's build this. So I can grab these two buttons and drag them in the bottom left. Again, you can see the constraints are doing exactly what I would expect. I can maybe add a button for changing the mode of my table view to sort differently or show different data. And that can be my share button. And then, of course, we'll want an actual table view.

Grab that guy, drop him in. And again, you can see how easy it is to create these interfaces. As I'm snapping the guides, constraints are falling into place and just doing exactly what I'm looking for. All right, so I want to change the titles of these buttons. Now, take a look at this button for a second. You can see there's a couple different constraints going on right here. A bunch of different spacings between my super view, and more interestingly, this spacing between this other button.

As Ken mentioned, dynamism is one of the features of Autolayout where the power really shines. As the content of your interface is changing at runtime, this interface is just flowing around it. This doesn't just help you at runtime, but also helps you at design time. So I'm going to change the title of this button from "button," which is not incredibly descriptive, to "add." Now, something really interesting happened there. Not only did the button shrink to fit its contents, but if you're watching closely, it also pulled this button right along with it.

Let me undo and redo. And you can see that button just gets pulled along with it because of the spacing constraint that must hold between those two buttons. So I'm going to go ahead and set the titles for the rest of these. And watch as I change these titles. You can see the button is going in exactly the direction that I would expect, given the constraints that are on that button.

And there's my share button. All right, so let's go ahead and run our app and see this guy resizing. And look, that's resizing exactly how I would expect. No work needed. Just drag my stuff out. And it's -- hmm. It's kind of doing what I want, but I really don't want the change mode button to overlap with that remove button.

So how might we solve that? Well, one option would be to write a lot of code. Or what I could use is I could insert my own additional constraint. Xcode will help you by adding automatic constraints as you're resizing and laying out your views. But there will also be cases where you want to specify additional relationships. So I can do that right here.

can select the remove button and the change mode button. Go up to the editor menu. And there's an add constraint menu under the editor menu. And you can see there's a couple of different kinds of constraints that we can add. We can add one for width, for height, for spacings between views, for spacings to the super view, and also making things equal widths. And Ken will go in more detail about some of the different possibilities of constraints that are out there.

These are just a few. So I want to add a horizontal spacing constraint. What I've done here is I've taken a relationship that's currently on the canvas, for example, the spacing between the remove and change mode buttons, and I've frozen that into a constraint. So as I mentioned, constraints are real objects in my document.

So I can select them, but I can also inspect them. And constraints have some really cool properties that are incredibly powerful. So let me open the attributes inspector and show some of them to you. So this shows me that I have a horizontal spacing constraint that is equal to 119 points.

[Transcript missing]

All right, so let's just start over.

Really encourage you guys, as you hit bugs in Xcode, because there really aren't any, so you have to really try hard to find them. But if you find any, we really encourage you guys to file bugs, and also to include really reproducible steps, and that will really help us out. All right, so now I can hit Command-R and run my application. And now when I resize my window, perfect. It stops resizing exactly how I'd expect when that minimum distance is hit.

There's one more interesting thing going on here. If you notice, I can't resize the window any smaller. It won't go smaller. But I've never set an actual minimum width for my window. This is another one of the powerful features of Auto Layout, is that as you're resizing your window, it's taking into account the constraints in the views in that window.

So as soon as the constraints in the window can't hold anymore, the window just automatically stops resizing. This minimum and maximum width is no longer some static property that you have to kind of fragilely calculate based upon maybe your largest strain that you're going to show. But rather, it's just automatic based upon the constraints.

And you can see this if we go back in Interface Builder. And I select these views. You can see there's a spacing here and here and here. And also, I specified one to center that button. So all those, again, are relationships that must hold. So in order for them to hold, the window can't get any smaller.

But is that really what I want in this case? There's a lot of wasted space when I size my window small over here. I kind of want the window to be able to get smaller, but I also don't want to break into my constraints, because constraints must hold.

Constraints have a property called priority. And that allows you to say, well, this constraint is required in the default case, but it's actually optional in particular cases. Let me show you how that works. So I can select this centering constraint. And you can see there's a field here called priority.

My default, all the constraints that you create or that Xcode helps you create are required. They have a priority of 1,000. Anything less than that means that the constraint is optional. Autolayout will try as hard as it can to meet that constraint, but if there's circumstances which would prevent it from holding, then it will break since you've specified that it's not required.

Cocoa specifies a couple of predefined priorities, kind of like NSWindow levels. And if you put your priorities in between these, you can get particular behaviors. In this case, I want my window to be able to resize, but not clip any of the content of my buttons. If you'd like more information on what those different levels are, just check out the documentation. In this particular case, a value of 400 just falls inside of that range. Again, check out the documentation to see more of what that means. And you can see, if you're working in Xcode, we'll draw those constraints that are optional with a dotted line.

And just because I know you guys are all thinking it, I'm just going to add that final spacing constraint as well. We're going to do the same thing by just making that greater than or equal to the standard space. And now, when I run my application, You can see that it's resizing normally. Now when I hit that minimum distance, that centering constraint is broken, but also that other spacing constraint is held.

So just look at this interface. I mean, it's so cool that we could do this without writing a single line of code. And that's some of the power of Autolayout. Ken, back to you. Thank you, Kevin. Okay. So thank you very much, Kevin. Let's take a look at the API for this, since so far we've only seen it in Interface Builder. So the one new object we have, the guides need to be captured as NSLayoutConstraint objects. That's the only new class we have.

And what has to go into it? How are you going to create these things? Well, it turns out that every single thing you saw drawn in IB, every one of those constraints, can be expressed pretty much as a little equation of the form y=mx+b, where by y and x we really mean attributes of views. So for example, if you have this relationship, we're talking about how the left side of the Accept button should be equal to the right side of the Cancel button plus 12.

And you can see how that's expressed in one of these little equations. So this is pretty much what we have to capture. And this is what, if you're using it in code, this is what goes into a constraint. Besides that, as you saw, there are just a couple more properties you need. There's the priority, as Kevin showed you, and then there's the relationship.

So is it equals, is it greater than equal to, or what? So if that's what you need to capture, then this is what the creation method's going to look like, right? And that's right. So to make this particular relationship, you're saying Accept button left equals Cancel.right plus 12. So that's normal.

That's what Cocoa would usually do, right? Except this is maybe not so good, if you look at this. And we actually think this is very important. So the problem is, if you look at that code, is it very simple to tell from what you've written there what's going to come out in the UI? And I would say no.

It would take me a long time to read that. If I were scanning through the code, I would have a hard time with it. And it would be really easy to blow a sign or to use the wrong attribute or something like that. And we think this is actually pretty critical.

So I'm going to try to emphasize this, because it runs through the whole API. It's really important that whatever you're doing with layout, you have to be able to visualize it. And linear source code that just runs from left to right, top to bottom, is a really poor way to visualize geometric UI.

So if linear source code is so bad for this, and you can't see what's going on, how can you make your source code more visual?

[Transcript missing]

So this is actually a little language. It's a format string. We call it a visual format language. So let's go into it a little bit. So I'm going to use a slightly more complex one just so we can break it apart and see what the pieces are. The two primary pieces, in some ways, are a view and a connection.

Views appear set off in square brackets to make them look somewhat more rectangular. Connections are set off by hyphens because they look like connections. In both cases, you can put a predicate on it. Here, what this is saying-- if you put the predicate on the view, like this, this is saying the cancel button is width 72. The space between the cancel button and the accept button is 12 points. And the accept button is width 50, is what this particular string says. And this is a constraint creation method. So it returns an array of constraints, three in this case.

OK, so let me actually-- the language is very small. It's really just driven by what looks good on the page. And so let me go through the whole thing by example. If you do want an actual formal grammar, that's in the documentation. So first of all, the original example I gave you had the hyphen but didn't have a number on there.

And that's because the hyphen by itself means the standard Aqua space. And this is very dynamic. It's based on the individual views involved. And it's also based on-- it's also evaluated very late. So if we actually change what the meaning of the standard space is for a particular control or a particular situation, this would pick it up, which is great, if that's what you mean.

And if that's not what you mean, don't use the standard space. Use the-- use something explicit. If you want to-- so here's one showing width. So we have a view that we're saying should be at least 60 points wide. And this one has a priority. So again, the default priority is going to be required. But if you say it's at 700, that means it's at strength 700.

Okay, if you want to make a connection to your super view, which is pretty common, well, we figure if you're connecting out to the outside, it looks kind of like a wall. So the vertical pipe character is the wall. So here you're saying the edge of the super view, five points, then the content view, then five more points, then the other edge on the other side. For vertical layout, this was clearly a question, how should we do this? But we really lucked out because Ake Inoue was working on vertical text support in Cocoa this release, so we figured you can just write your text just vertically right in Xcode.

You can already tell that's not what's on the slide. So what's on the slide is that if you start it with V:, that's vertical, and it's always red from top to bottom. So here, the thing on the left, turn it sideways like that to see what it sort of really does. So that's not, you know, it's not quite as good as horizontal layout, but you can still see it pretty well. If you want to say that two views are flush, so the space between them is zero, just write them next to each other without the hyphen.

And if you want to say that two views should be the same width, you can do that like this. So right where you'd normally write something like equals 30 or greater than equal to 50 or whatever, the equals is actually optional, but I would use it in this case. Here you're saying the yellow box is equal to the, the width of the yellow box is equal to the width of the red box.

The predicates are actually lists, so you can comma separate them and say something like this, so you have some view that needs to be at least 30 pixels wide, but no more than 80. And, like I said, it creates a whole array of them. So, and this is where it really comes together.

So, if you see this on the page, this is laying out a whole line where it says you've got the super view, and then the standard space, then the find, and then standard space, find next, standard space, a find field, which is at least 20 points wide, and then the other edge of the window. So, and you can kind of see what that's going to do, right? I mean, just look at the correspondence between the ASCII art and the actual visual layout. And that's what we're trying to do. So, I hope you like that, because we think it's a lot better.

OK, now one more thing you might be wondering about this is those names. So this is a string, and we're referring to things like cancelButton, acceptButton. How do those mean something? And that's this views dictionary parameter pass at the end. And the views dictionary is something that needs to map those strings that you see to the actual object.

So if you were to print it out in debugger, you'd see the acceptButton is some button. And because that would seem to be a little bit of pain to make, we have some conveniences to help you do that that I'm going to talk about some, and you can also see it in the documentation.

[Transcript missing]

Now, one of the things you might want to do is actually mutate one of these constraints, besides just add them or remove them from a window. I didn't mention this before. I didn't say that these properties on, I didn't say whether constraints were immutable or not. And the answer is that they're basically immutable. You can't change much of anything, but you can change the constant.

And the reason why there's that deviation is that it turns out to be, the primary reason, I'll get to the slide in a minute, is that it's algorithmically important. So it turns out that we can make incremental changes like that much more quickly than if you were to remove constraints and add new constraints that had the same values. This is how window resize works.

That is the actual, as you saw, the way a window will stop sizing down, that's because there are certain constraints on there that say the window wants to be this big with a certain priority, and those priorities are defined in our API, and that's how you work with it.

Anyway, so, but one of the things you might want to do is animate a change. And since in Cocoa previously you do this by using the animator proxy on a view, since you can't do that anymore really, it doesn't really help, instead use the animator proxy on a constraint.

[Transcript missing]

And that's dynamic. So now if somebody says, I want these two objects to have their baseline aligned in the controller layer, there's still interaction with the view layer where the view can say, well, this is what my baseline is. So it really separates things and lets you vary the view layer independently from the controller. Another one, so alignment rects. So again, suppose that you wanted to lay these two views down next to each other with, say, the bottoms aligned, and you want to do it in code.

You think that would be pretty easy, right? Because you just make two rectangles which have the same bottom. Well, that's not actually going to work, because if you look at these red lines I'm drawing, these are the frames of these two controls. And you can see they're not tight on what you would think of as the content. And there are two reasons for this.

In the case of the segmenting control, you can see there's some little shadows there, right? And a frame has to contain all of the pixels that draw, so it just has to be big enough for that. But you don't care about the shadow for layout. And with the button, the issue is that it's somewhat historical, that we haven't been able to change metrics for controls, and this button just used to be larger.

We can make things smaller, but not bigger. So how do we work around this? Well-- not really work around. How do we deal with this? We have an API for specifying the alignment rect of a view. And this is, again, an overrideable view method, where you say what your insets are from the frame to the alignment rect. And the alignment rect is supposed to be sort of the visual extent of your control. So it's ignoring shadows, ignoring engraving lines, ignoring things like that. It's this tight box.

And here we're drawing the baselines as well. And the reason the baselines are drawing is that I actually generated the screenshot, not in Keynote, but with a real live screenshot. And the way you do that is by passing this argument, NSView show alignment rects yes in Xcode, or wherever, on the command line.

And what that'll do is it'll make these things draw. And that's useful because when you're designing your UI, when you're writing the actual view subclass, you need to make sure that the alignment rect is where you intend it to be. And all of the layout-- actually operates on this, not on the frame.

So when you say you want two things bottom aligned, you're always, always talking about the alignment rect, not talking about the frame. And so this is the overridable method. And you don't need it unless you're actually writing a view subclass, and unless your view subclass has shadows or something. OK, those two are probably easier to understand than this one. But this one's really important. So this is the intrinsic content size of a button. This lets a view declare-- I have a natural size.

OK? It's kind of like size to fit in a way, right? It's like saying, because of the stuff I have in me, because of my title, because of my images, whatever, this is the best size that I can be. But we invert the relationship between size to fit in classic Cocoa layout and with the Auto Layout in that previously you had to say, size yourself to fit right now.

Now things always size to fit all the time. OK? So this is the overridable method. And you can do this by setting up constraints that say, no, I actually want you to be this big. And the way that works is that you override this method intrinsic content size.

And then Cocoa will call that method, and it'll set up constraints that say, I don't want to be bigger than this, and I don't want to be smaller than this. And you'll see how this works. So this really reduces the burden on the controller layer again, because in the controller code, that means-- you saw that code I wrote to lay out that first line of buttons, right? It means you don't have to talk about the size.

You don't have to talk about these things you don't know about. You don't know about-- the controller code doesn't know about alignment racks or baselines or this intrinsic content size, and you shouldn't have to. So it makes it much faster to write these things and to change the two different-- the view layer and the controller layer independently.

So as I was saying, though, so the reason I was saying the way I did, that it sets up constraints that say no bigger than this, no smaller than this, is that even though that's true, while the view knows what that size is, the view doesn't know how important it is.

Or it can take a guess, but the guess won't always be right. So for example, a button typically shouldn't clip its contents, right? And in fact, should resist the window sizing if you're trying to bring a window down. So what that means is that the prior, we call it the compression resistance priority for that button.

Should be a higher priority than the priority with which the window size is down. And it is, by default. But sometimes, like in this case in Finder, you actually do want to allow a button to be clipped. And the button's not going to know that. So in that case, you need to be able to set the priority. And that's typically something that the controller layer does, if necessary.

And then the other side of that is that typically controls don't want to be bigger than they really have to be either. And we call that the content hugging priority. And I, at least, am very proud of getting the word hugging into the API. Also, you can talk about how strongly you want to hug, which is kind of nice.

Okay, so that's it for intrinsic content size. And I know it's a little bit abstract, but it's really important. It's really part of this whole layering thing. So the last thing I want to talk about along this line is the relationship between intrinsic content size and fitting size. Like I said, so we don't really do size to fit anymore in this new metric, but there's still a useful thing you might want to say.

So what is the best size for this area of the screen? Okay? But you know what? You can't possibly really mean best size. Because with Autolayout going, the best size for something is always going to be the size it is all the time, right? So what did you really mean? What you really meant, I think, is if you just look at that subtree of the view you're interested in and everything in it as well, all those constraints, how small could that be and still be good? That's what you really mean.

And we call that the best size. We call that the fitting size, which is just an inversion of size to fit because we're returning a size rather than doing it. But the difference here is that the fitting size is something you can call and it uses the layout system.

It invokes it. Intrinsic content size is something you override and you feed data into it. And the intrinsic content size is not something where you're going to be looking at your subviews. The intrinsic part of that is that it refers to your own content, not some other view's content.

Okay. So that's what I had to say on layering. Now I want to show you a little bit about debugging, because it's going to be very important. If we have a system that's trying to do more on your behalf, you need to be able to tell what it's doing, and introspect, and make sure you can fix problems if you see them.

And to do that, I'd like to do this with a demo. And it's not really just going to be a demo for debugging, it's also going to be a demo for all of the stuff I've been talking about just now. It's just I won't do it all right, and then we'll have to debug it. Okay. So let's get rid of that.

So this is sort of a version of sample code that we already have up on the web. This is called Find Panel Layout. And what we're going to do is we're going to try to lay out something that looks kind of like a standard find panel, okay, that has a find button, a find next button, and a field in the first row, and then a replace button, a replace and find button, and then a replace field in the second row.

Okay, and there's a little bit of boilerplate in here that I put in to begin with. And this is just creating... Wow, it looks really big in this template. This is just creating the views, okay? And there's nothing really interesting about this. This is the same as you'd always do it before. There is actually one interesting line, but I'm going to skip past it.

Set translates auto-resizing mask into constraints. We will talk about this later, okay? But for the most part, this is just making a bunch of buttons and text fields. And let's see what it looks like to actually do the layout of these guys, okay? So first of all, we're going to create these things, and we're going to create them using the visual format syntax, okay? And just look how reasonable this is, I suppose, okay? So we want to say we have the edge of the window, standard space, then the find button, standard space, then the find next button, then the standard space, then the find field, And we'll say that needs to be at least 20 points wide.

And then we'll have standard space out to the edge. And I hope I don't have a typo here, but if we do, we get to see some of that debugging support too. It gives you a nice error message and good diagnostics if you make a mistake in the string.

Okay, now besides that, I want all of these things lined up by baseline. So I'll say layout format in this options mask, layout format align all baseline. Metrics, I'm going to skip over, take a look at the documentation on that. But then I do need to pass this dictionary I talked about, this views dictionary, which makes these strings mean something.

Okay? And for that, I'm going to -- oops. We're going to use this macro that I sort of alluded to before, which is this NSDictionary of Variable Bindings. And what this does is this makes a dictionary that maps find, the string find, to the find object, the string find next to the find next object, and like that.

So it's just a fairly convenient way to make a dictionary like this. And we'll see, because we can continue to use the dictionary over and over, it's pretty reasonable. Okay, so that makes an array of constraints. Now I need to install them. So I'm going to install them on the content view of the window.

Like that. Oops. I forgot to set up hex codes prefs the way I wanted. Okay, now, so that's the first row, and you can kind of see. So it's a find, then find, next, then find field. For the second row, we'll do the same thing, except we want replace, and replace and find, and then replace field.

And then we need to do the vertical layout so that these things have some vertical positioning. And the way we do that is so V:, so now this is the top. Now we want the standard space, then we'll do the find field. Oops, field. Then we'll do the standard space and replace field.

And then we'll say we want at least 20 points before the end of the window. And I will get rid of this baseline alignment, because that would complain. And let's see how we're doing if we run this. Okay, that looks pretty good. You know, I resize it, it stops. That's pretty much what I want. Except that -- so, you know, hopefully that's good, right? I mean, it looks like what you wanted. But, of course, this doesn't look the way real fine panels look. We want these two to align.

So let's do that. So I could just add another line here that specifically just align those two things. But because I already have this, I'm going I can say I want, just as I said, layout format align all baseline here, I can say layout format align all left to make sure those two fields are aligned.

Except I'm not actually going to say left, I'm going to say leading. And so this is an attribute, all of the attributes by the way are left, right, top, bottom, middle, middle X, middle Y, and baseline. And then also leading and trailing. And I'm going to hold off on explaining leading and trailing for a minute.

Okay, but now if we run that, okay, much better. That's pretty much looking like what I want, except I do actually see something a little bit weird here. Why is the Find button so wide? I can see that the whole space here needs to be at least this big because the replace and replace and find limit it, but why isn't the space distributed here? To do this, I want to show you some debugging support. I could instead just figure it out, but let's not do that. Some debugging support, first of all, is this method, constraints affecting layout for orientation.

This says I want to know all of the constraints that are currently affecting the Find Next button's horizontal layout. This isn't the constraints that involve the view. This is all the constraints that are actually affecting it. That would include things like just the size of the window, if that did affect it.

I could just log these out, and it would print out using the ASCII art syntax, but that's not really good enough either. Like we were saying, one of the things that's really driven us during this API is visualization. We actually have this method on this window, VisualizeConstraints. all this, check out what you get.

You get this beautiful MOV window, okay, right at runtime, and you've basically entered, like, a constraint debugging mode, okay? And you can see you've got some drawing here, and what these guys are drawing is they're actually drawing my constraints. Now, and what you can do is, what's good about this is it really helps you figure out, so which one's wrong? If you're just looking at a bunch of constraints in the debugger, well, what are they? This lets you know, okay, these are the constraints I'm looking at. Now, both of these constraints look reasonable to me. I do want these spacings here, but I see this button, Exercise Ambiguity. Okay, let's click that and see what it does.

Ah. OK. So what this is saying is it's saying that given the constraints you set up, there's no difference between this layout and that layout. It means you didn't put enough constraints in place. And if you think about it, that's right. So you've said this total space must be at least this big, and the Find button wants to hug its contents, and the Find Next button wants to hug-- oops. I forgot I couldn't click those-- wants to hug its contents. But they want to hug equally strongly right now. So which one wins is just kind of arbitrary. And that's when you get this. So let's fix that. OK. So we can fix that by-- oops.

by saying we want the Find and the Find Next button to have the same width, but at a pretty low priority. Because we don't want it to-- if we had it at a full-- actually, I'll show you at a full priority. So we'll do it for the next row of constraints as well.

Okay, so we want the Find to be the same width as the Find Next button, and the Replace button to be the same width as the next one. But if I just make those required constraints, Well, that's not going to do what I want, see? Because now we just get a whole block here that's all too wide. So really, again, I want to use this notion of priority and say this doesn't always have to hold.

But if you don't have anything else-- but once you've satisfied everything else that you really need to satisfy, try to satisfy this too. And so now we get that. So now the Find and the Find Next button are the same width, which is what you want in this interface. So I hope that's good.

Okay, now one more aspect of debugging that I'd like to show you before we move on is, that's what happens if you have not enough constraints. What happens if you have a wrong constraint? Okay, so what would happen if I just said the Find Next button should be with 200? Okay.

I run that, and indeed, there's a constraint there, but suppose that it's wrong, okay? But you can see these constraints, and now this one looks kind of suspect. What's that all about? If, you know, clearly, since I just did this, I know where it came from, but if we didn't know where it came from, if I click this, We can see it.

So what I would do is I'd go and I'd call this method visualize constraints with the constraints affecting the layout. And I can map back and forth between what I see in the UI and the actual objects by clicking on them. And so that would let me see like, oh, yes, this doesn't look right. This is negatively impacting my interface. So that's a constraint which is bad. So that's letting you map from a view is in the wrong place to here's a constraint which is wrong.

Okay, that's all I want to show you about that. Debugging typically breaks down into a number of phases. So the first phase is given a bad layout, you need to find a view whose frame is wrong. Okay, so something looks wrong to here's a view that's in the wrong spot.

Sometimes that's really easy, right? When we were just looking at this, we knew these buttons were in the wrong spot. However, if it's not easy, there are a couple of debugging things you can use to help. First, if you pass the arguments NSShowAllViewsYes, that will make a frame draw around every single view, which makes it easier to tell when you have invisible views or deep containment which one's actually wrong.

And you can also use this method on NSView. Both of these have been here for a long time. Underscore subtree description. If you use this in the debugger, this will print out a string that gives you a nice, concise way of seeing what all the frames are in a subtree hierarchy.

Clearly, that's not API. It has an underscore, but we do tell you about it. It's just we don't want you to get caught up in the middle of the process. We don't want you to bake it into your source code. We don't want you to ship it. It's been in the release notes quite a few times. OK, so now if you've gotten as far as here's a view which is in the wrong place, the next thing you're going to need is here's a constraint which is wrong.

OK, and this I already showed you. So we have two bits to help you do that. First, there's constraints affecting layout for orientation. That's going to give you sort of candidate constraints, one of which might be wrong. And then we have this visualization UI, which can let you help understand what those candidates are and figure out which one of them is actually incorrect.

Now the last thing you need to do is once you have a bad constraint on your hands, you need to figure out some code which is incorrect. And we, I sort of showed you that over there, but it was rather, in the case I showed you, it was really easy. It was like, oh, I just typed the wrong thing, I know where that came from.

But what if it's not easy? You know? What if you've been calling set constant a bunch of times, or like, you click on this thing in the UI and it's like, yeah, that constraint's wrong, but I have no idea where it came from. Okay, so for that, what we have is we have support instruments that looks really quite a bit like zombies or like leaks debugging.

What it does is if you run, if you start instruments and you see there's a new basic template called Cocoa Layout, and what it'll do is it records all of the interesting events in the lifecycle of a constraint. So it'll record creation, it'll record being added to a window, it'll record every time the constant is modified, which is what we're seeing here.

So what you should do is start your application, and you're going to have a little bit of a delay. So you can start your app under instruments, reproduce the problem, find a bad constraint, now filter down using that search field on the right so you can see just the constraint or constraints that are causing you trouble. And you can filter down based on any data that's in the description because we log out the whole description to instruments, so anything there.

And once you've got that, then when you select them, you can see the backtrace for each line on the right, so that lets you see backtraces for all the places where interesting things happen. So that lets you figure out where it came from or where it went wrong, and that's the code you have to fix.

So that's the debugging support. Hope it works. We think it works. But if it doesn't, let us know. All right. The last major topic I want to talk about is transition. Okay? So since this is such a big thing, how do we go from where we are, which is classic Cocoa Layout, to where we want to be, which is the new Auto Layout type stuff? So, and for this, the key observation is that everything, and Kevin kind of mentioned this before, everything you can represent with an autoresiizing mask, you can also represent using constraints.

Okay? So in the case of something like this where you've got things, you're saying strut, strut on the top right, what that says is it should be, you know, the space from my super view to me is fixed, and also my width and height are fixed. And that can be done as constraints.

And by default, if you just make a view in code, it is. So there's a property, translates autoresiizing mask into constraints. And if this property is true, then we will take that autoresiizing mask, and we will use it as constraints. And by use it, I mean we will actually generate constraints that map to it. And then when you call set frame, it does actually update these particular constraints. Okay? And this is a little bit confusing. It's because it's part of the transition. Transitions are always a little messy. It'll go away when you can stop worrying about it.

Uh, right, so, uh, however, you usually wanna turn this off because the auto-sizing mask, if it's active, is going to completely determine the location of your view, and that's not really gonna be a good thing for the most part because you actually wanna use the system, right? You want to, uh, any other constraint you could introduce at that point would be at best redundant. It's really not gonna help. Um, so, uh, so you wanna turn that off, generally.

This was the line I skipped past in the find, in the demo I gave you, uh, I was turning this off. Now, an interface builder, interface builder will already set this property to, to know for you, and most of you usually create an interface builder, so you don't usually have to do anything. But at least for now, if you're creating your views in code, you do have to turn off, uh, this translation.

Okay, now the one other aspect of the transition I want to describe is the fact that the whole thing activates lazily on a per-window basis. So I showed you that these are the three phases that happen when a window needs to display. If you get update constraints, layout and display.

If nobody has added a constraint to a window at all, then these two passes don't happen, which is just exactly the same way as it was in Snow Leopard. And what that does is it lets us pretty much guarantee complete binary compatibility. I shouldn't say complete, I'm just tempting the gods. But anyway, it makes it very good for binary compatibility.

But what happens is that as soon as anybody in a window has created a constraint and adds it to a view, then this kicks in at the window level. It doesn't really matter because, or shouldn't matter because of the translation business that, you know, it doesn't really matter if the window is using layout or not. But for debugging purposes, you certainly might want to know this. know this.

Okay, that is everything I want to talk about today, except that I want to look at the review and hopefully try to bring this together, right? Because we've looked at a lot of stuff, but I hope I convinced you that it's a pretty good thing. So, first we were claiming that we can reduce the need for in-code layout, that we can use IB, that it goes further. And you see why this is true? As you're changing content and things like that, it doesn't matter. You still want the same constraints as you said in IB, so it's fine. You can use IB for a lot more problems.

If you're using in-code layout, first we have this visual format syntax, the ASCII art that lets you really visualize it, and it's also just shorter. And so, get through that quicker. And also, you just have to do less, because views themselves can take a little bit more responsibility for their layout through these things like the baseline and the intrinsic content size and the alignment rect stuff. The design flexibility. Again, now if you want to update your artwork or update the way a control looks, you can do that without updating all the places that use it.

This is something you've never had in Cocoa. If you want to localize, obviously that's much better. All you have to do is swap the strings usually, and that works through these same mechanisms like intrinsic content size. And you do have more expressiveness. So, Kevin was showing this to you. There are some UIs that you can create using priority and using inequalities that you would have a hard time, and also just peer-to-peer relationships, that you would have a hard time creating.

In classic Cocoa layout. But in some sense, you know, these bullet points don't really quite capture it, right? I mean, what we really think is that this is just the right way to do it. This is the right layering that will feel good the same way as when you first learned, say, delegation and notification or model view controller, that these things really cohere. And that they make a good way to program. And we think it is. And our goal has been to make the best layout system that exists anywhere, and I hope we got there.

So, you know, let us know. File bugs. So, for more information, you can talk to Bill Dudney. He's our evangelist. You can look at the documentation. We do have documentation. There's a programming guide. I don't know if you've seen it yet. Actually, I'm not even sure if it went public until just now. We also have release notes, and we also have sample code. You definitely want to look at the sample code. The best way to find this stuff is to go to the Mac developer site and to search for auto-release. I mean, auto-layout. .

Sorry. If you're interested in the basics of how we're doing this, because I didn't really tell you how this actually works in terms of the algorithm. The algorithm comes from a terrific paper written by Drs. Alan Borning and Greg Badros out of the University of Washington called the Cassowary Linear Arithmetic Constraint Solving Algorithm.

And please go take a look at that. It's very interesting. And really, the reason this is on the slide because I wanted to have a chance to thank them. And thank you to Dr. Borning and Dr. Badros. And last, the developer forums and also the Cocoa Dev mailing list as soon as this has gone public. And you can talk about it in public.

You know, it was really hard to bring this stuff down just to a presentation. I know you're going to have questions. You should have questions. And bring them, OK? I have mail rules set up. I'll see every single question that gets sent to one of these forums that includes a keyword like NSLayoutConstraint. OK, related sessions.

Kevin's going to talk again. Just right after lunch. And he's going to do a little bit more AutoLayout stuff in Interface Builder. And that's really what you want to orient around. So go see that. That is it. So thank you very much for coming. And I hope you like it.