Speaker 1: Good morning. Welcome to Best Practices for Mastering Auto Layout.
I haven’t even done anything yet. It’s for OS X and iOS. My name is Peter Ammon, I’m an engineer on the AppKit team. And we’ll be showing you Auto Layout which is a way to make your layouts simpler to write, simpler to modify and easier to understand.
The point of this session is to raise you from the level of someone with some Auto Layout familiarity to a true Auto Layout master.
As you know, Auto Layout was a feature we introduced in Mac OS X Lion. We’re very happy that we could bring it to you in iOS 6. But this session will cover both and the reason it can cover both, AppKit and iOS is because they have identical APIs ... almost identical APIs.
The most significant difference of course is view. When I say view, you should think NSView if you’re working on AppKit and UIView if you’re working on iOS 6 and any other differences I’ll call out when we reach those parts.
We’ll just do a brief review, hopefully this is stuff you already know. Auto Layout introduces just 1 new class in its layout constraint. A constrain expresses a geometric property of a view. For example, this view’s width is 120 points, it can also express relationships between views. For example, foo’s width is 120 bar’s width is equal to foo’s width, now bar has a width of 120 as well.
And relationships has a coefficients and a constant. We could say that foo’s width is twice bar’s width minus 20 but we could not for example say that foo’s width is the square root of bar’s width.
Now, constraints can be equalities or inequalities, so we can say foo’s width is at least 120 and constraints have priorities. Foo’s width is 120 with a low priority and 75 with a high priority. The 75 priority will dominate, it doesn’t try to average between them, it just winds out entirely and foo will have a width of 75.
You can create constraints in interface builder with a visual format language and with the permit of API and when you’re designing how to create a constraint, prefer to create them in that order because that’s the easiest order, it takes the least work and it’s the lease air prone.
That concludes the background review.
In this session, we’re going to cover the thinking and constraints. How do you adjust your thinking for and Auto Layout world? We’ll talk about debugging constraint based layouts. What happens when something goes wrong? And lastly, we’ll really start to leverage constraints in Auto Layout and get some mileage out of this new feature.
Thinking in constraints. Now you can use Auto Layout just like you’ve been using springs and struts by specifying constraints for the offset and width but if you are able to shift your thinking to a declarative type of layout, it will be far easier and we'll see an example of that right now.
Let’s say you have a keyboard type view. You have a bunch of keys that are all the same width and height and they’re in this container view and there’s some padding between the keys and the container view has and overall width. How would you implement this layout with springs and struts? Well you would start by computing some sort of expression for the width. You’d say that the width of a key is equal to the total width divided by the number of keys minus some fix up for the padding. And then for each key you might compute an offset which is going to be the key index times the key width plus some sort of fix up for the padding and I’ve written plenty of code like this, you probably have as well and it works but it’s not easy to tell by looking at the code what it’s actually doing. It’s hard to revisit this and say “Oh, this is laying out a keyboard type view.” How much you address this with Auto Layout? Well, you can start by establishing with constraints for every key and we could use the similar … the same expression. So you’d say that the key width is the overall container with divided by the number of keys minus some padding.
Now, for the offset, instead of calculating the offset, you could just string the views together. You could say Q’s offset is equal to its container’s left edge plus padding. W’s offset is equal to Q’s max X edge plus padding and etc. and they spring all the views together. This is better because these are relationships. If the container width changes, you don’t have to do this work again, it will do it for you automatically. The width of the key is related to the width of the container.
This is still not great, right? There’s still this sort of confusing expression, hard to tell what that’s doing. Here’s how an Auto Layout master would approach the same problem. If you listened carefully, I told you in the beginning exactly what constrains we need. Every key has the same width. Let’s just establish constraints that say Q’s width is equal to W’s width. W’s width is equal to E’s width, etc. And the last step is to add a constraint at the right edge binding the right most key to the right edge of the container and when you add that last constraint, it’s going to unfold all the views like an accordion and you’ll get exactly this layout. And notice that there’s no real calculation here, it’s just very simple relationships which I think is a hallmark. When you reach that state, you know you found a good layout.
Also notice, there’s no rounding. We didn’t talk about what happens if the number of keys doesn’t evenly divide the amount of space available but with Auto Layout it will handle that for you.
I’m going to give you an example or a demo rather of a layout very much like this in a real app.
This is an app called Opposite Subtract, just a very simple game. The point is you have a bunch of letters that … by the way, everything you see here is done entirely with Auto Layout, naturally. The idea of the game is to find the word which is the opposite of the word in the upper left in as little time as possible and every time, every few second your score goes up and when you get the correct word your score goes down because opposites. The opposite of warm is cool, so I got that right, it explodes. The opposite of big is little. T, t, you can drag this around a little. Little, I got that right and explodes.
You see that the view at the bottom here, I’m calling it the rack view, has a layout very similar to the key view we saw before in the slides. Notice that when I start adding views, it’s going to start shrinking to accommodate the extra space, but eventually it’s going to not let it get too small and it’s just going to start clipping off the right side like that. I can add them back. You’ll also notice that they will try to grow with the … let me get rid of some more. The keys will try to grow with the view and shrink with it but they also have a minimum size when they start clipping and a maximum size and when they reach the maximum size, they stay centered.
It looks like a very simple view but there’s actually a lot of sort of sophisticated edge cases and aspects of the layout going on here.
Allow me to show you how I implemented that. The pile up here, I’m calling it the pile and the rack down here are both subclasses of a view called letter container and letter container has a method add letter view and when you add a letter view to it, top or the bottom, it’s going to add it as a sub view, add an object to its own internal store and then it’s going to remove all the constraints its established for all of its letter views, get rid of them and call, setting these update constraints.
The next turn of the run loop the constraints will be updated. I’ll show you the constraints we actually add. We pin the first view to the left edge, that’s why the P is over on the left. We pin the last view to the right edge weekly, so we add objects from array with constraints that say the last view is pinned to the right edge of a super view with a width of at least … with a padding of at least 20 and the priority 450 which is a low priority. We make a constraint that says the first letter view is the same height as the container but weekly so, so that’s why it tried to be the same height as the rack and we do that by saying that the height is equal to the height of the container times 1 minus or plus 20. We do that with the weak priority as well.
Here we centered the letter vertically in the container, so we say that center Y is equal to the center Y of the container view and here’s where we established constraints that make all the views equal width like we saw on the slides.
The other thing I wanted to show you, we didn’t say everything about the height of the views but in the letter view itself, when it instantiates itself, it establishes a constraint that say my width is equal to my height and also establishes the min width and the max width. This idea that different aspects of the program can be responsible for the layout is something we’re going to be revisiting.
The layout became distributed in this app. Another component of the app responsible for the layout of these views is the component that does the animation. I think of it … if you can decompose your layouts into components where each component can own an aspect of the layout, that’s how you can get a really clean layout.
I think of it kind of like a retain release or arc where the lifetime of an object is sort of the global problem, it’s kind of a hard problem but each owner of an object can contribute to that lifetime and overall lifetime is established by looking at all the interested parties.
We saw that the Q view said “I’m square, width is equal to my height” and the super view said that “Oh, you’re the same height as me and also you’re vertically centered in my” and that’s sufficient to establish the layout.
Let’s say you’ve figured out the layout you want, but now you want to convert from the springs and struts to this new layout. How do you go about converting an existing app? The first step is you want to plan you attack. Do you want to just convert part of a view or 1 view when you can use Auto Layout just in the part you need it which is convenient but you can also, if you do a full conversion, it will pay off later.
The next step is to turn on Auto Layout in your nibs. That’s easy to do. It’s in the left most tab, the nib inspector, there’s a check box, use Auto Layout, there it is. It’s a per nib property not a per window preview property. When you check the check box, interface builder will create the constraints that reflects your existing layout, but what it will not know how to do is how that layout would change if your screen rotates or if you resize the window. You might want to inspect the constraints interface builder is made and then add to or modify them.
The next step is to find every place for you to create a view and turn off auto resizing mask translation. This is something you heard about in the first session. You have to call set translate auto resizing mask in the constraints to know and that means I want to use only Auto Layout for this view and if you forget to do this, you’ll figure out pretty quickly because you are likely to get unsatisfiable constraint errors. We’ll see some of those later.
Now you want to identify places where your existing code performs layout. Layout subviews and iOS is a good candidate or any sort of call to set frame or set frame size or set frame origin. These are all places where existing code is doing layout and they all have to go. But what do you put there instead? Well what you don’t want to do is just take the code and try to replicate what it does, instead you want to try to understand the layout that the existing code is an implementation of and recreate that with Auto Layout and the hope is that by using Auto Layout, this will become simpler. The idea of Auto Layout, it’s to simplify, make these sophisticated layouts.
It’s great if you can replace it with nothing. If you’re working around limitations in springs and struts, maybe Auto Layout can handle that directly or if the existing code is implementing a relationship that Auto Layout can express directly, then you could just use Auto Layout to express that. But if you have to add some constraints, well then naturally you should do so.
You want to think about which component should own each constraint as we saw the letter view owns its aspect ratio but a super view owns its positioning and its height and you want to consider centralizing all these work and update constraints, that will keep your code, your focus, all our Auto Layout stuff will be in 1 place, we saw that with the letter rack view before.
And of course, the last step is to test it. Make sure you have the layout you want. If you have … if you see log method is … you may have unsatisfiability. If you see views that jump or disappear, you may have ambiguity. When you see those issues, you just need to fix them. How do you fix them? That brings me to debugging. What can go wrong in an Auto Layout app? Well 1 problem is that you have constraints that don’t provide enough information and that’s called ambiguity. The sort of the opposite problem is that you have constraints that provide conflicting information, that’s unsatisfiability and the third type of problem is that constraints that are not ambiguous and are satisfiable but they are satisfied in ways you may not expect. We’ll see examples of all of these.
And keep in mind that when you use interface builder, it will address these problems for you. It will not let you create unsatifiable constraints and it will not let you create an ambiguous layout. This is just one of many reasons why you should prefer interface builder when doing Auto Layout.
And keep in mind that you might think “I can’t use interphase builder because I’m going to need to add or remove the constraints or I need to adjust the constraints constant.” But you still can, you can reference the constraint with an outlet.
Ambiguity. Ambiguity happens when there’s multiple layouts that all satisfied the constraints equally well. For example in our key view, in the slides, maybe the layout was this or maybe the layout is that. We didn’t say anything about the vertical positioning or the vertical size of any of the views. And the common symptom is that your views will seem to jump between different layout that all satisfy the constraints or they’ll just disappear entirely which usually means they’ve jumped to size 0.
And when this happens, it usually means you need to add more constraints. Just like a rack has 4 properties, X origin, Y origin, width and height. You need to specify 4 properties of ever view but they don’t have to be those same 4 properties. You could specify the center X and the width or the min X or max X or max X and width, any combination will work.
And inequalities are usually not enough, so just because I said that this view’s width is at least 20, that doesn’t mean it’s going to try to be exactly 20, it’s just as happy being 20 as it is 40 or a million. Inequalities cannot fully specify the layout in most cases?
Now, sometimes, ambiguity can come about because you have priorities that are equal and does not know which constraint should satisfy. For example, if we say that view’s width is 24 with a priority 500 and view’s width is at least 30 with the same priority, well it can’t satisfy both because 24 is not at least 30 and have equal priorities, so it doesn’t know which to constraint to prefer and this is the case where you have ambiguity.
If we were to raise the priority of the inequality to say 525, it still can’t satisfy both, but now it know which one to prefer. Let’s go in to satisfy the inequality first, after that it’s going to try to satisfy the equality as close as possible. There is no ambiguity here and the resulting width is going to be 30 because 30 is a number which is at least 30 and closer to 24 than any other number.
If you want to know if you have ambiguity layout, you can just ask you view has ambiguity layout and if you want to know what’s ambiguity about it, you can call exercise ambiguity in layout and what that will do will change the frame to another layout that satisfies the constraints equally well. You can also call visualize constraints on OS X only, this will give you a sort of a purple overlay window which will tell you if you have ambiguity and allow you to exercise it in that way.
By the way, these functions are really useful for debugging but they’re only for debugging, you should never have a need to call them in production code.
That covers ambiguity.
Let’s talk about the other problem. Unsatisfiability.
A layout is unsatisfiable when there’s no layout that can satisfy all the required constraints and the keyword there is required. Only required constraints can contribute to unsatisfiability. And keep in mind that constraints are required by default, so you’d create a constraint with the base API or the visual format language and you don’t specify priority, it’s going to be required.
And keep in mind that sizes are required to be at least 0 even if you never establish a constraint. If it’s possible that the layout could be satisfied with the width of -10, that’s not something that will allow, that will be an unsatisfiability case.
Now, when you get an unsatisfiable layout, it will get an exception immediately, it reports that the very same call where you call add constraint to the view. But ambiguity is not reported, it’s just something which happens. We can use this to our advantage, we can temporarily tolerate ambiguity.
You saw in the letter rack view that when we added a new view, we removed the existing constraints, that forced the layout ambiguous but that’s okay because we were about to reestablish those constraints the next turn of the run loop. The reason we remove the constraints is to avoid any risk of even transient unsatisfiability.
What do you do when you don’t see anything? This is a common problem with Auto Layout. It’s possible you views are hidden or they don’t exist at all and it’s also possible that they do exist but they have a 0 width or 0 height or both. You should never call set frame in Auto Layout but you can ask a view for its frame. Where is it? What’s it width and height? What its offset? You can also ask what constraints are making the view that size? So this is view constraints affecting layout for orientation on OS X and the constraints affecting layout for access on iOS and you pass horizontal or vertical and if you call this in the debugger, keep in mind that horizontal is 0 and vertical is 1 so you don’t have to type out those long constants. And it’s also possible that the layout is ambiguous and you want to check if it has ambiguous layout and you can exercise it to see.
And keep in mind that some layouts are only satisfiable at 0 size. You might say that foo’s width is twice bar’s width and bar’s width is 3 times foo’s width. You might be thinking, “Oh, there’s no way that can be satisfied,” but in fact it can be satisfied with everything equal to 0. This is another reason why you might have a view that just don’t appear.
Let’s see a demo of all of these cases.
Let’s say we’re working on this app for a while. I’m going to switch to a different branch. Ambiguity. And when I run this, it’s going to tell me … it’s not going to tell my anything rather, instead I just don’t see anything. There’s a couple of different problems that could be going on here. I’ve added the menu item debug. When I run debug, what it’s going to do is it’s going to take the first view, the reason you knew object to see erase syntax, to get our first letter view and we’re just going to log out its frame and then for every view in our pile, we’re going to ask if it has ambiguous layout and if it does, we’re going to call it exercise ambiguity in layout. Let’s try that.
Choose debug. My views up here, they have stupidly tall frames and I see that the initial frame height was 0. This tells me that the height is ambiguous and we need to think about who should be responsible for setting the height. I could have done something very similar by calling visualized constraints and here, we could pass an array of constraints to visualize it. I’m just passing an empty array. The reason to do that is because it will show me this purple overlay window which tells me layout is ambiguous here and I can click this button to exercise ambiguity which doesn’t actually seem to do anything, sometimes multiple layouts all have the same visual look. This would be a case like that which is why I called exercise ambiguity on ever view, every one in my letter tiles.
In this case, who’s responsible for setting the height? Well the height should be equal to the width of every letter, so I’m going to remember where I set that. I set that on the letter view itself and in it, that’s part of the aspect ratio, someone had commented that out. I remove the comments. I run that and I’m back in business. That’s an example of ambiguity.
I’m going to switch to a different branch.
This time, when I run it I do see my views but they’re all pinned to the top and they don’t have the layout I want and in addition, there is this enormous amount of text which has been output to the log. This is a case of unsatisfiability, you can tell because it says unable to simultaneously satisfy constraints and we’ll go into these log messages in detail because it’s very important that we’d be able to read and understand these, but for now we can just look at what they’re saying.
We see that letter view A, so the A tile, it’s center is equal to the pile’s center plus 56. We see it’s also equal to the pile center plus 172. Clearly we cannot satisfy both of these constraints, so we have unsatisfiability. I suspect that the positioning here is due to the letter pile setting the offsets of the letter views, so I go to the letter pile and I say okay, here is the center X equal to my center X. here we have the center X again equal to my center X but this time we’re using the constant Y instead of X. this looks like a copy and paste error. This is another illustration of why using interface builder or the visual format language would have avoided this error entirely. I’m going to say the center Y is equal to my center Y plus the location in pile, hopefully we’re back in business. I still have some ambiguity which I’m not going to try to debug right now, but we can see we certainly have an improvement, letters are no longer positioned in crazy places.
Okay. And the last case, I’m going to start up the app and everything seems fine at first but when I click a view, uh-oh, my window suddenly shrunk to some crazy width. What happened here? And if I add more views, it’s going to change along with it. And I can resize it but I can’t resize it very much. This is the case where we have constraints that’s not unsatisfiable and it’s not ambiguous but it’s layout satisfied in a way we don’t want.
When you see something like this, a good thought is that some constraint has a higher priority than it should and in this case, it has a higher priory than the priority with which the window holds its width which is why it can shrink the window.
In this case, because it only happened when they added the view to the letter rack, I can … reason that is probably related to one of the constraints the letter rack adds and I see that … oh, the constraint putting the last view to the right edge has a padding of 20 but it doesn’t have a priority. That means it’s required which is a much higher priority of course and what the window hold its width.
I’m going to first make this an inequality and I’m going to make it a lower priority than 500 which is the window holding priority. And this time when I run that, the views start out and they no longer shrink the window. Those are some examples of how you would debug problems you can encounter with Auto Layout.
We saw with the unsatisfiability case a very long log message and you’ll probably encounter this at some point and it’s intimidating at first right? It’s like a wall of text that comes at you and when you see this, you don’t want to panic and we’ll break it down for you right now. He first thing to notice is this set the user default and ask constraint based layout, visualize mutual exclusive constraints, he set this to yes, the instinct can trace to come unsatisfiable, it will open up that purple overlay window and it will call visualize constrains and you can actually inspect the constraints visually. This is a good thing to have set under application all the time when you’re debugging.
Also notice [inaudible 00:26:48] exception throw and exception is thrown but the second layout becomes unsatisfiable. Now, the exceptions immediately caught so it does not propagate through your code but this is still a very useful fact to know because it allows you to put a break point there and see the back trace of “Oh, here is where I added that first bad constraint.” Now this is telling you what it’s doing to resolve the conflict which constraint is allowing to win and this is usually not that interesting. The real important information is the array of unsatisfiable constraints at the top, unable to simultaneously satisfy constraints.
Let’s zoom in on one of these.
And there’s 3 parts to a constraint message. The top we have the address of the constraints, so if you reference constraints, you create and you can ask is this my constraint or someone else’s? At the bottom we have a mapping from identifier to view. If you set identifier for your view, it will make your log messages easier to read. You set the identifier in interface builder right here under the view inspector, there it is. This is a good thing to do for all your views. Instead of seeing a long hex address, you just see the name of the view.
And the most important part of course is the meet of the constraint itself, what relationship it expresses. This has the views identifier and it has the property of the view, the attribute, the relation, the view it’s related to and then the constant. It’s very useful to translate this from sort of code into English. We would say “Oh, the letter view’s center should be 11 points to the left of the pile center.” Now for constraints that can be expressed with a visual format syntax it will show that syntax in the log messages. For example, in this case, we say that view’s width is 250 and again, it’s really useful to translate this into English.
Here’s another case, we say that the view is left or right edge is at least 50 points from the … another view’s left edge. And here’s a very important log message you’ll see, it looks like this, it’s a little different. The first thing to notice is that it’s not in its layout constraint, it’s in its auto resizing mask layout constraint. And this tells you right away that translates auto resizing mask and the constraints is on for this view. If you didn’t mean to leave that on, you know right away how to fix this, locate the view and turn that property off.
Next you’ll see the auto resizing mask itself. One auto resizing mask, it actually generates multiple constraints, so this tell you which of the many constraints from this mask, this particular constraint is. And lastly we have the familiar visual format language. This views height is 50.
Here’s a … longest example, the last example we’ll look at. What can we see … what can we tell from this constraint? Well we see that it’s an auto resizing mask constraint. Here’s it’s auto resizing mask, we see that it’s a horizontal constraint because H means horizontal, V means vertical and we see that there’s 200 points between this view on the right and what? Well the vertical bar means super view. 200 points between the view and a super view and the super view we see below is an instance of flip view, that’s its description. To put it all into English, this view’s left edge is 200 points from that of a super view which is a flip view. And by saying it in English, it’s often very obvious why the constraints are not simultaneously satisfiable.
So that concludes debugging. Hopefully by now you’ve got a fully debugged layout and you’re going to need to start really leveraging it and getting some mileage out of the future.
One very common task on OS X and iOS is animation. How do you animate layout changes?
Well one approach is to apply the new layout and let Core Animation handle it, just like in my call set frame, you could just apply the layout and it will animate to the new position and this is very fast but it has the disadvantage of perhaps [“transiently” 00:31:08] violating constraints because it just interpolates from the old position to the new position without regard for constraints.
You can also animate the constraints directly and this is pretty fast and it produces a correct layout at every frame. What do I mean by this [“transient” 00:31:23] violation or correct layout?
Say you have a view like this with a purple view inside and a blue view on the outside and the purple view wants to have it with the 100 but weekly so and it wants to have a padding of at least 20 on its left and right side and that’s requires.
As the view shrinks, initially the purple view is going to maintain its width but as its out of view shrinks, eventually the purple view is going to start shrinking with it as well.
If you were to animate this change using Auto Layout, that’s exactly what you see. Every step of the animation would be as if you had resized the window point by point. But with Core Animation, you’ll see something like this, where the purple view will have interpolate it from the start position to the end position at the same time as the blue view and it will not be exactly the same.
When you’re considering how to animate, consider whether you care about this sort of transit violation or not and then choose the API appropriately.
Let’s say you’re satisfied with Core Animation, you find this easy to use, you’re familiar with it, how do you do animation with Auto Layout?
Well, you want to set up your new constraints and then within an animation block, you call layout if needed or layout subtree if needed depending on your OS.
Here it is with NS View, you call Run Animation Group, you can set the duration, you’ll set allow simplicit animation and you call layout subtree if needed.
In UI View, it’s a little simpler, you animate with duration and you call layout if needed.
Now, maybe you do care about the transit violation or you want to use Auto Layout for your animation as well. How do you go about doing that? Well most of … and its layout constraint is immutable, you cannot change it once it’s been created, except for one property which is called constant, ironically. At a cost it can be modified after creation and when you modify the constant, it is very efficient because it doesn’t have to recomputed a whole new layout it can just use the work that’s already done and just tweak it a little bit to reflect the constant change, so this is efficient.
You could modify this on an NS timer, for example every tick of the timer you add 10 to the constant of a constraint or you could use the animator proxy on OS X only. You could say for example, constraint.animator.constant equals 10 and it’s going to step from its current value to the new value using the animator proxy. OS X only.
Let’s see a demo of these different animation approaches.
There’s 2 separate animations in this app or there’s a couple but one of them is … when you click a letter, it flies off to the right and then it flies into the rack and those are separate animations.
Let me show you how I implemented those. In the application animation, we’re going to move the letter from to the rack. The motion out of the pile view is done by animating and its layout constraint not using core animation. We start by computing a constraint that expresses its existing layout. Here’s that constraint, so we say that it’s actually flushed against the right side and the constant we is initially 0 but then we adjust it so that it pushes it back to where it’s supposed to be. I noticed that this animation constraint is created in this function and its local … entirely local to this function, so it’s pretty cool how you can add an aspect of the layout to the app and just have that localized in 1 function.
Then we add the constraint to the letter pile and we’d run the animation group, we set the duration to something that we like, we set the timing function to something we like and then we call the animation constraint animator, we set the constant to 0. Initially the constant is something large and negative, so the view is a negative distance from the right side, it’s inside the view and then it’s going to be pushed towards the right side of the view.
Once that’s done in the completion handler, this is the animation where all the letter flies in and by the way, you might have other views animating in there as well. You see that if I start adding more views, all the views are going to animate together.
We’re going to remove the constraint to push it outside of the pile, remove the letter view from the pile, add it to the rack and we’re going to run an animation where all we do is call layout subtree if needed and this will apply the new layout and it will animate from its current layout to the new layout. This is very little work to do an animation.
That covers animation. Let’s shift gears to talk about writing a custom control that you want to integrate with Auto Layout. How do you go about doing that?
Well there’s a couple of concepts you should be familiar with because they’re very important and the first is alignment rects. You … we’ve been talking constraints like they’ve been operating on the view’s frames but that’s actually not quite true, they operate on the alignment rect and you can think of the alignment rect as the content area. If the user sees the button, push me, that’s good but we don’t know what the frame is. The frame could have a very small padding or no padding or a lot of padding. But in a sense, you don’t really care in Auto Layout, you want to position the content not its logical frame.
Again, the frame is that logical rack and the alignment rect is the rack within that which contains the actual content.
Here’s another example of alignment rects. Say you have a text and you have an image and you want to align the tops and bottoms of the text and the image. Let’s say you want to apply a badge to the image like that, that badge isn’t really part of the image, it should maybe overhang outside of it, but it should not contribute to the alignment at all. In that case, the alignment rect is still the gear that we saw before and excludes the badge.
You could convert between alignment rects and frames, there’s a method, alignment rect for frame and frame for alignment rect and that’s all.
The other important content to know is the intrinsic content size. This is something you encountered a little bit in the introductory session.
Many views are very happy at any size, if you just make an arbitrary view, it doesn’t really care what size it is, but some views have a preferred size and you may know this as size to fit or size that fits and in Auto Layout this is the intrinsic content size.
Now you may have an intrinsic content size in 2 dimensions like this button has a preferred width based on its label and also preferred height based on its bezel art work. This progress indicator doesn’t really care what its width is but it has a preferred height and this is just a base class view, it has no preferred width or preferred height. And when you have an intrinsic content size in one or both dimension, Auto Layout would generate constraints for you in the dimensions where it has that intrinsic content size and there’s 2 constraints per dimesion.
Here we see the button’s width is at least 120 and that’s called the compression resistance because it tries to prevent it from getting smaller than 120 and it’s also has the width of no more than 120 and this is the … called the content hugging because it tries to hug its content and prevent itself from getting bigger.
Notice that this is sufficient to unambiguously size of view. Before I told you that inequalities are not sufficient to avoid ambiguity but in this case they are because there’s only one number which is at most 120 and the least 120 which is of course 120.
You might be thinking well why didn’t we just set the width at 120? Why do we have 2 constraints? And the answer is because they can have different priorities. This is a very powerful tool you can use in Auto Layout.
For example, if you have a text label, maybe you’re okay if the text label grows as the windows grows or if the screen rotates because it looks fine but what you really want to avoid is the text label clipping because then you’re losing content. You would implement this by setting a low content hugging priority so it allows itself to grow but a high compression resistance priority so it does not allow itself to shrink.
For the button case, you would have a high compression resistance and a high content hugging because it wants to be its preferred width very strongly.
Now the intrinsic content size is not settable. You can’t tell a button what its width should be except by setting constraints on it, its size which is intrinsic to the content. But the priorities are settable. This is a very powerful tool in your arsenal. You can set them directly in interphase builder right there under the sizing tab, you see that there’s a sliders for vertical and horizontal. You can also set them in code. You say set content hugging priority for dimension and you pass a priority in the dimension and the compression resistance priority and in UI kit it’s not dimension is access. Otherwise it’s very similar.
Now let’s say we have a text label like this and it starts out and it’s exactly the right size but the content changes. Now it’s clipping because it still has that constraint setting it to the old size. How do we resolve this? Well anytime the content changes such that its intrinsic content size should also change, the view will call and validate intrinsic content size on itself and this tells Auto Layout “Oh, I need to get the intrinsic content size again and establish constraints reflecting it.” And if you’re implementing a custom control, this is a great method but you want to … you don’t want to ever override it but you want to call it on yourself whenever you’re intrinsic content size might change because some property review has changed. For example the text or image content. In this case by calling [inaudible 00:41:56] intrinsic content size, it will establish new sizing constraints and the view will be at its correct size.
Now intrinsic content size also works in … with springs and struts and you could use this to your advantage as a better size to fit. What do I mean by better? Well size to fit cannot change. It has to stay the same to preserve binary compatibility. You recall on OS X, we used to have pop-up buttons with these large blue sort of blob on the right side with the indicator and that’s gone now, but the pop-up button is still sizing itself as if it’s there for binary compatibility reasons.
As the art work changes, size to fit can sometimes leg behind but intrinsic content size can change and in fact it has change in mountain line to reflect our work changes and also just to fix some bugs. You can use intrinsic content size as a better size to fit. Doing so is very easy, you first you make a rectangle which represents the intrinsic content size and you want to convert from the alignment rect to the frame and then you could set that frame on the view. By doing this you often get a better layout even in springs and struts.
When you’re writing a custom control, rather than establishing with the height constraints on yourself, it’s often very nice to just override intrinsic content size and if you do that, it’s a good idea to measure your text or your image, your other content and you could also just hard code values. If you have art work which is exactly 22 points tall, you can just return the height of 22 for your intrinsic content size.
What you don’t want to ever do is inspect your core position or size, it’s supposed to be intrinsic to your content, should have nothing to do with where you’re currently at. You don’t want to call super and just tweak its value, so you don’t want to say “Oh, my intrinsic content size is super class in terms of content size plus 3,” and you don’t want to use it as a substitute for explicit constraints. If you’re just making an empty view which should have a particular width, just set a constraint on that view, don’t override intrinsic content size.
And for indicating your alignment rect, for example you have a shadow or the image badge, the dos are similar but you want to consider using the default implementation which is just … returns the same thing as the frame. You can also override alignment rect in sets which is just a set of 4 values which can be used to convert to and from frames in alignment rects. This would save you from having to override frame for alignment rect for example.
And the don’ts are the same. You don’t want to inspect your position size or your constraints because those should be irrelevant. You don’t want to call super tweak its value, you just want to set your own value and you don’t want to use it as a substitute where you should be using explicit constraints.
The most sophisticated thing you can do as Auto Layout custom control is to override layout or layout subviews in the UI kit and this is how you do most of the sort of fancy frame dependent layouts.
All layout does is set the receiver’s frame to the values determined by the constraints and on UI kit, it sets the receiver center and bounds to the values determined by the constraints.
And afterwards, the constraints and the frames agree and as long as you ensure that’s still true in your override, you can do anything you want.
Here’s an example, say you have a view like this, imagine like a tool bar view and it’s got some views within it and it’s all strewn together with constraints like we saw in our keyboard view. But let’s say if the super view shrinks, you want that right most view to just go away and if it grows, you want to bring it back. How would you do this with Auto Layout? Well you do this by overriding layout or layouts subviews and you call super and then you’d inspect the frames of all your views and you say “Oh, this right most view is actually outside my bounds, I’m going to get rid of it, remove the view, remove these constraints and then you can call super again,” and you can do this over and over again until you found lap that you’re happy with.
I’ll show you an example of that.
You recall that in my letter rect view at the bottom as I start adding views, eventually it’s going to reach them [“in a room” 00:46:27] size and then just start clipping. And let’s say I think all that clipping looks kind of dumb. Instead of it clipping, I want to just hide the views, get rid of them. I’ll show you how we could do that.
I have this code here.
The first step is to override layout in the super view. We call super layout for any view that we threw up before, we’re going to add it back into our view and call a big constraint or subtree if needed, so this re-establishes all the constraints for all these views that we added back and we’ll call layout and then animate from block of the 0 duration, so you don’t see this kind of transient work being done.
And then afterwards, in the loop, we’re going to get our last view, last object and if it’s max X is less than or equal to our max X, well we’re done, it fits. But if it’s not, that means it’s the overhanging and we want to get rid of it, so we call the remove letter view, we add it to our overflow array, we call update constraints for subtree if needed and then we call layout again in an animation block with 0 duration and we’re going to keep doing this until the right most last view was either nil or it’s fits within our view.
I’ve added this code and [inaudible 00:47:50] run it and add a bunch of views and then when I start shrinking it, it’s going to shrink until it reach the minimum size and then it’s going to … instead of clipping it’s just going to throw the view out.
Here we have a layout dependent view hierarchy.
Let’s go to the last topic which is internationalization.
How do you localize an Auto Layout app? Well Auto Layout makes it a lot easier because controls know what their content should be. You don’t have to go into the nib anymore and adjust things by hand for every localization and in particular, the same constraints all work across different localizations.
For example, if we have buttons cancel, save, and then the edge of the window, under German, the content was going to get a lot longer because German is typically has longer words but the constraints are the same so you’ll get the layout appropriate for the content. This is pretty cool.
Hopefully saw in the developer kick off that one nib could now service multiple localization and the control content comes not from the nib anymore but from a string’s file which is associated for every localization and you can still use separate nibs when necessary. And this even works for right to left.
Auto Layout is cast not in terms of left and right so much as leading and trailing and leading and trailing flip under right to left. This exact same constraints will give you a layout that flips under RTL languages like Arabic or Hebrew.
I’m going to show you what happens when I use some of my app.
My app is using this new localization mechanism. You can see we have a base localization then we have English and German, so it will create strings files for these, so here’s the base localization and here’s English and German and this is all of the control content, just laid out for me.
Let’s say that I want to change the title of my initial window to something longer. Now going to run it, all the windows size to reflect this news room without having to recompile the nib and this works for the course because I had constraints that say that the text field needs to push out the window if it … if it’s below its intrinsic content size.
What I can also do is turn on right to left and you can do that with some user defaults Apple text direction yes, force right to left routing direction, yes. And when I run this, it may look the same, well it shouldn’t look the same. What’s different is that the buttons that used to be on the right are now on the left and the buttons that were on the left are now on the right. When I start the game, the words have flip sides and in particular when I click a letter, it’s going to fly onto the left and not the right, so I did absolutely nothing to support right to left but I still got a pretty awesome right to left experience. See how the views are appearing on the right and not the left and everything else about it is the same.
We think this is a way easier and more powerful way to localize applications.
In summary, layout allows for sophisticated and powerful layout with less code or no code in some cases compared to springs and struts and you could take maximum advantage of it if you’re able to think declaratively about your layout, not imperatively.
Be wary of issues like ambiguity or unsatisfiablity, but log messages are there to help, so when you get one, you want to take the time to read it and understand it by overriding methods like intrinsic content size, you can integrate your custom control with Auto Layout and it will save you a lot of work and now you can localize with a single nib file and multiple strings files.
For more information, contact our Evangelist Paul Marcos, see the Coco Auto Layout guide, it’s really great, it’s got a ton of complete information. You don’t have to write down the address, you can just Google Coco Auto Layout guide. The developer forums as well, of course.