Introduction

With the release of Xcode 5 and iOS 7 the need to learn, understand, and use Auto Layout become critical. This post reviews the changes to Auto Layout in Interface Builder (IB) in Xcode 5. This article is based on my experience using Auto Layout on a very large iPad app built using Xcode 4 and examining how I would build parts of that app if I had Xcode 5 from the start.

What is Auto Layout

Auto Layout is a constraint-based layout system available in iOS 6.0 and above. In Auto Layout you describe the relationship between views and iOS calculates the location and size of the views at runtime. This approach allows the layout to react to changes in screen and content size without requiring your code to change. It is important to note that Auto Layout is the only way that views are laid out in iOS, and while you may specify the layout using springs and struts in IB, but iOS always translates them to constraints at runtime.

In iOS 6, Auto Layout was available and recommended, but not required. In iOS 7, Auto Layout has become more important, to the point of practically being required, because it facilitates the ability, provided by Dynamic Type, for users to select the font size to use in standard button, label, and text field controls, and then have the rest of the screen respond correctly to the change in font size..

A welcome change to Xcode 5 is greatly enhanced control over Auto Layout in Interface Builder. In Xcode 4, the creation of a slightly complicated view was close to impossible because Xcode did too much to ‘help' the developer, but didn't give the developer any way of correcting it's ‘help'. Xcode 5 still has the ability to help the developer, but it only does so at the request of the developer. Yay! We humans are in control again.

Because of Xcode 4's over-helping in IB, many developers frequently reverted two the two other ways to implement Auto Layout based views: Visual Format Language (VFL) and manually instantiating NSLayoutConstraint objects. Thankfully, because of the improvements in IB you'll probably not be using these methods as much in the future. I've found that views that were impossible to build in Xcode 4 IB can now be created quickly and easily in Xcode 5.

Auto Layout uses NSLayoutConstraint objects to describe the relationship between views. A single NSLayoutConstraint object can only describe one relationship. To accurately specify the positioning of a view the constraints assigned to it must resolve to an X & Y position and a width & height. iOS evaluates all of the constraints in a view hierarchy, finds a valid solution where all of the constraints can be satisfied, and positions the views based on that solution. It will use priorities assigned to the views to arbitrate conflicts in the resolution of the constraints. If you don't specify a priority then iOS assigns the highest priority. If any one of positional or dimensional constraints are lacking on a view iOS will consider it an ambiguous layout and the position and size of the view will be indeterminate. If a view has conflicting relationship definitions, for example two constraints, with the same priority, that define the width, then iOS will log an error describing the conflict and discard one or more constraint that is causing the conflict. In some situations you may have multiple constraints that evaluate to identical answers, but iOS may still consider those to be conflicting.

Thinking in Auto Layout

One of the problems that have beset developers adopting Auto Layout is the need to change how you think about the position of our views. Before Auto Layout you thought about frames and bounds; size and position was everything. However, you must now think in terms of relationships rather than sizes.

I've seen two ways to think about relationships in Auto Layout: outside-in and inside-out. The outside-in way of thinking starts from the superview and applies relationships down the view hierarchy until you reach the lowest level UILabels, UIButtons, and other UIKit views. I've found that using this approach alone can easily lead to setting sizes on those lower level components, which violates the principle of thinking relationally.

The inside-out method starts by letting the lower level components specify their desired sizes via their intrinsic content size. Then their containing views are allowed to grow to contain their size or to limit their size based on external limitations (like the screen size).

The truth is that both methods end up working together to define the layout. The resolution of a constraint equation works both from the inside-out and outside-in. Constraints on subviews can limit the size of the superview and constraints on a superview may limit the size of its subviews.

There's now a new way of thinking about laying out views in IB. In Xcode 4, as you placed or moved views in a xib, IB would continuously recalculate the constraints required to make the view as you had laid it out. This had the unfortunate side effect of IB constantly fighting what you wanted to do, and if you had manually created constraints it could delete them willy-nilly. It also seemed to add constraints that were not necessary. Thankfully, IB doesn't do that anymore.

Now, as you position views in IB it makes no assumptions about what constraints you want. You can get all of your views exactly where you want them (it still provides helpful guides to let you line things up). Once you've placed a view you can then ask IB to create the constraints to create the position you want, or you can specify those constraints yourself. If you create a constraint it will not remove it unless you ask it to.

I've found the best approach to be:

  1. Place the views where you want them
  2. Manually add constraints to each view to give it the behavior you want
  3. Let IB add missing constraints
  4. Test in preview mode

Quick Tour of XCode 5 Interface Builder

When you open a xib file in XCode file you get a familiar looking screen. The most significant change is in the lower-right of the layout area. Apple has completely reworked the controls, shown in the Figure 1.

Figure 1: Auto Layout Controls

In the example for this blog, we'll be defining constraints to layout a 10-button numeric keypad that resizes as it's containing view resizes. In Xcode 4, this particular view was impossible to build using IB.

The Add New Alignment Constraint button (the left most button in Figure 2) in the controls brings up a menu that allows you to constrain views in relation to other views. These controls now allow you to specify a constant value to go with the constraint.

Figure 2: Add New Alignment Constraints Popover

Each of the items in the popover menu allows you to define alignment constraints between two or more views. For example, if you have two views selected and choose the Leading Edges checkbox, then IB will add a constraint to align the views' leading edges. If you specify a number in the constant box to the right, it will adjust the constraint by the value of that constant.

When you add constraints to views using this method, you have the option of updating frames. By default, when you add a constraint this way, IB will not reposition the affected views to reflect the behavior of the constraints. The Update Frames control has 3 options:

  • None: No views are repositioned
  • Items of new constraints: Only those views that have constraints added will be repositioned.
  • All Frames in Container: All views in the container are repositioned, whether views were added or not.

Be aware that if you use items 2 and 3 while the constraints are still ambiguous your views may appear in odd places or become invisible. I've found it best to wait until I think that I've fully specified the constraints before instructing IB to reposition the frames.

Many of these same relationships can be defined by control-clicking on a view in your layout and dragging the cursor to the related view. This works with any view in the hierarchy including the containing superview. In Figure 3, I'm connecting the buttons for 5 and 6.

Figure 3: Constraint Context Menu

The next control, the Add New Constraints control, in IB, show in Figure 4, adds new spacing and sizing constraints to your views.

Figure 4: Add New Constraints

The top portion allows you to specify spacing between views. You can specify default values or specific values in each of the combo-boxes relating to a side of the view. If you've only selected one view, the spacing applies to relation between the selected view and its superview. The spacing combo boxes will also present values used in other constraints within this same view.

The 2nd section allows you to specify the width and height of the selected views. I would think twice before using this type of control because it will quickly lead to thinking about frame sizes rather than relationships. If you're only thinking about frame sizes you'll end up creating a view that doesn't size with new devices.
The 3rd section allows you to define relation ships between view widths and heights, and the 4th allows you to relate edges or centers of views.
The next popup menu is the cleanup menu, shown in Figure 5. It helps you see what IB is thinking about constraints, request IB's help, and to clean up general messes.

Figure 5: Constraint Cleanup
The Update Frames option will move all of the selected views to the positions specified by their constraints. If you have ambiguous views then this option may make views disappear or move out of bounds. This option makes no changes to your constraints.
The Update Constraints option does the opposite of Update Frames. It considers your manual placement of the selected views as gospel and updates constraints to match your layout. It does not add new constraints; it just updates the current constraints to match any manual movement of the view.
The next option, Add Missing Constraints, will add enough constraints to the selected views to remove any ambiguous layout problems. This is similar to what IB in Xcode 4 did when you manually moved a view.
The Reset to Suggested Constraints removes all of the constraints you've added to the selected views and does all of the thinking for you. If you liked IB in Xcode 4 then you'll love this option. I suggest avoiding this option.
The Clear Constraints option removes all constraints from the selected views. This is a good way to get a do-over on a messed up layout.
The last group of options is similar to the first except they apply the action to all of the views in the selected view containers (view containers are the top level views).

Diagnosing Problems

IB in Xcode 5 provides some helpful diagnostic information to help you see if anything is wrong with your layout.
First, IB provides a helpful indicator, shown in Figure 6, on the view hierarchy if a view has problems. Click on that tiny red arrow to show a list of problems.

Figure 6: Problem Indicator
The list of problems can be anything from missing constraints, which is serious, to frames that are not where the constraints will place them. The misplaced view messages (shown in Figure 7) can be resolved by using one of the several Update Frames operations to relocate the views. You can use the Add Missing Constraints or Reset To Suggested Constraints options to fix missing constraints. Warning: resetting to suggested constraints could be dangerous to your layout and sanity.

Figure 7: Problem List
An orange dashed border, shown in Figure 8, on a selected view indicates that the view's current frame does not match the location as computed by the current constraints. If you Update Frames on that view it will relocate to it's computed position.

Figure 8: Misplaced Frames
A yellow solid frame on a view indicates that the view is under specified. See Figure 9 for an example, The red dashed rectangle shows the ambiguously computed frame for the view. You should take a close look at the view and determine which constraints need to be added.

Figure 9: Missing Constraint
Red constraints, shown in Figure 10, indicate an over-specified and conflicting constraint configuration. You should determine which constraint is in error and delete it or lower its priority.

Figure 10: Constraint Conflict

Laying out a complex view

A practical example of the power of Xcode 5's Interface Builder is its ability to layout complex view structures. In Xcode 4, it was close to impossible to maintain control of the constraints when doing complex views. If your view had more than a handful of sub-views things quickly got out of control. For this article we'll show the full layout for a 10-button keypad.
One of the best practices recommended by Apple is to recite the behavior of the views then use those described behaviors to specify your constraints.
In the spirit of that best practice, this paragraph will describe the behavior of the keypad. The keypad will be a 10 number keypad structured so that the keys resize to fit the view containing it. All of the natural numbers (1-9) buttons will always be the same height & width and will have equal spacing between the buttons and the containing superview. The 0 button should be centered in the bottom of the view and extend to the edges of the view with the same spacing between it's leading, trailing, and bottom edges as between each of the natural number buttons. The 0 button will be the same height as all of the natural number keys.
Notice that I didn't specify any exact sizes, I'll let Auto Layout figure out what the exact sizes will be at runtime.
To begin the example, drag 10 buttons onto the containing view and give each a text title corresponding to its number (Figure 11). You should place the numbers somewhat close to where you want them to end up in the final rendering. They don't have to be exact; Auto Layout will remedy that soon. Don't let the buttons overlap because overlap tends to confuse IB.
Select all of the buttons in the view and then click on the Add New Constraints tool, as in Figure 11.

Figure 11: Layout Heights
Use the upper segment of the popover to set the spacing between all of the views to be the iOS standard spacing. If you want to change the spacing to something close or more spacious just change the ‘Standard' value to your own specific value. Also, select the checkbox to make all of the views have Equal Heights. Notice that I've left the ‘Update Frames' selection to None. If I were to update frames at this point the behavior would be unpredictable because all of the views still have ambiguous layout. After you've set the spacing and heights, press the ‘Add XX Constraints' button to add those constraints to the layout.
The reason that the buttons are ambiguous is because we have not specified a width for them. We cannot do that in the first step because the 0 button's width does not equal the other buttons.
To continue, the layout, select all of the natural number buttons and click the ‘Add New Constraints' tool, as shown in Figure 12. In the tool, just select that the buttons have equal widths. This will add constraints to the buttons specifying that those buttons are all equal widths.

Figure 12: Layout View Widths
At this point it is safe to update the frames on the buttons in the container. I've selected the ‘All Frames in Container' option for the update frames selection. This will move the buttons that you've laid out into their computed positions, shown in Figure 13. Each of the constraints on the views will be displayed with an icon indicating the type of constraint. If you select a single view it will display all constraints that are associated with that view. For example, in the view below I've selected the 4 button. IB shows the constraints on the 4 button and the 5 button because the height and width of the 5 button are constrained to equal the height and width of the 2 button.

Figure 13: Constraint Display

Conclusion

Apple has listened to its developers and produced a very usable and powerful tool for manipulating view constraints in Xcode 5. You will not have to fight it or avoid it as with prior versions of Xcode, but you should keep in mind some key principles for Auto Layout:
  • Think relationally; don't think about frames and sizes.
  • Don't think linearly; Auto Layout doesn't evaluate constraints in order. They are all considered equally, and conflicts are resolved via priority.
  • Don't over-specify constraints. Just say as little as possible to communicate the layout and no more.
  • Avoid letting IB define constraints for you. Unless your view is very simple, IB will probably get it wrong.