Working with Cocoa Autolayout for the first time is very different from the springs and struts model Cocoa developers have known for more than a decade. Despite the complexity of autolayout, I have found that you need to understand just a few basic rules to make the concept click. This article is an attempt to list those rules.
In each dimension (vertical and horizontal), each view’s position and size is defined by three values: leading space, size and trailing space.1 The leading and trailing spaces can be defined either in terms of a view’s superview or in relation to a sibling in the view hierarchy. Generally speaking, your layout constraints must fix two of these values so that the third one can be calculated. As a result, a standard view needs at least two constraints in each dimension for an unambiguous layout.
Some controls, such as labels and buttons, have a so-called intrinsic size. Depending on the control, intrinsic sizes can be valid either horizontally or vertically or in both dimensions. In the absence of explicit width and height constraints, a control’s intrinsic size functions as an implicit constraint. This allows you to create unambiguous layouts with only one explicit constraint per dimension (as opposed to the general rule above) and let the controls resize themselves automatically based on their content. This is the key for creating a layout that looks best in every localization.
The autolayout editor in Interface Builder seems to have a mind of its own. An understanding of why the Xcode engineers designed it this way will make working with it much less frustrating.
IB’s primary goal is to protect you from yourself. It will never let you create an ambiguous layout. That means that IB automatically creates constraints for you as soon as you place a view in a layout. Try placing your views along IB’s automatic guides to help it guess correctly which layout you want.
Use the Size Inspector to see a list of all constraints for a particular view. When the Delete menu item for a constraint is grayed out, this is IB’s way of telling you that deleting this constraint would create an ambiguous layout and is hence not allowed. You need to create at least one more custom constraint before you can delete this one.
To create a new constraint, select one or more views in your layout and use the three inconspicuous buttons in the bottom right corner of the canvas. These are easy to overlook.
Try not to set a control’s size explicitly. As long as you don’t resize them manually, most controls will resize themselves to fit their content and use this intrinsic size to create a perfect, content-sensitive layout. This is especially important for UIs that get localized. Once you (by accident or on purpose) manually resize a control, IB creates an explicit size constraint that is hard to get rid of. To return to intrinsic sizing, use the Editor > Size to Fit Content command.
Unfortunately, using autolayout with Interface Builder forces you to be careful. For instance, if you find that you need to replace one control with another, deleting the original from your layout can lead IB to automatically create a bunch of new constraints that you then need to modify manually (again) once you have inserted the replacement control. It is probably not a good idea to constantly try to optimize your constraints while your layout is still in a state of flux. Better to do it at a later stage when it is more stable.
Working with autolayout in Interface Builder can quickly lead to frustration, which is why more and more developers (Brent Simmons is one example) prefer to write their layouts in code.
Forget that the frame
property exists. Never set it directly. A view’s frame is the result of the autolayout process, not an input variable. To change the frame, change the constraints. This forces you to change the way you think about your UI. Rather than thinking in positions and sizes, think of the rules that specify a each view’s positioning in relation to its siblings and parents. It’s not unlike CSS.
For backwards compatibility of your code, the springs and struts model is still the default. For every programmatically created view that you want to use autolayout with, callsetTranslatesAutoresizingMaskIntoConstraints:NO
.
When writing your constraints, keep an eye on the debugger console. I found that the error messages Apple logs for ambiguous or unsatisfiable layouts usually help you pin down the problem very quickly.Apple’s debugging tips in the Cocoa Autolayout Guide are a great start.
Animations also require rethinking under autolayout. You cannot simply animate a view’s frame anymore; if you do, the view will snap back to its autolayout-calculated position and size as soon as the animation finishes. Instead, you need to animate the layout constraints directly. To do so, either modify the existing constraints (you can create IBOutlet
s for constraints created in IB) or add new ones, then sendlayoutIfNeeded
to your view from within an animation block.