Constraints
Now that you’ve read about the why of Auto Layout, this section introduces the what. Here’s the basic vocabulary you need to start talking about this technology.
Constraints, as you learned earlier, are rules that allow you to describe view layout. They limit how things relate to each other and specify how they can be laid out. With constraints, you can say “these items are always lined up in a horizontal row” or “this item resizes itself to match the height of that item.” Constraints provide a layout language that you add to views to describe geometric relationships.
The constraints you work with belong to the NSLayoutConstraint class. This Objective-C class specifies relationships between view attributes, such as heights, widths, positions, and centers. What’s more, constraints are not limited to equalities. They can describe views using greater-than-or-equal and less-than-or-equal relations so that you can say that one view must be at least as big as or no bigger than another. Auto Layout development is built around creating and adjusting these relationship rules in a way that fully defines your interfaces.
Together, an interface’s constraints describe the ways views can be laid out to dynamically fit any screen or window geometry. In Cocoa and Cocoa Touch, a well-defined interface layout consists of constraints that are satisfiable and sufficient.
Satisfiability
Cocoa/Cocoa Touch takes charge of meeting layout demands through its constraint satisfaction system. The rules must make sense both individually and as a whole. That is, a rule must be created in a valid manner, and it also must play a role in the greater whole. In logic systems, this is called satisfiability, or validity. A view cannot be both to the left and to the right of another view. So, the key challenge when working with constraints is to ensure that the rules are rigorously consistent.
Any views you lay out in IB can be guaranteed to be satisfiable, as IB offers a system that optionally checks and validates your layouts. It can even fix conflicting constraints. This is not true in code. You can easily build views and tell them to be exactly 360 points wide and 140 points wide at the same time. This can be mildly amusing if you’re trying to make things fail, but it is more often utterly frustrating when you’re trying to make things work, which is what most developers spend their time doing.
When rules fail, they fail loudly. At compile time, Xcode issues warnings for conflicting IB constraints and other IB-based layout issues. At runtime, the Xcode console provides verbose updates whenever the solver hits a rough patch. That output explains what might have gone wrong and offers debugging assistance.
In some cases, your code will raise exceptions. Your app terminates if you haven’t implemented handlers. In other cases (such as the example that follows), Auto Layout keeps your app running by deleting conflicting constraint rules for you. This produces interfaces that can be somewhat unexpected.
Regardless of the situation, it’s up to you to start debugging your code and your IB layouts to try to track down why things have broken and the source of the conflicting rules. This is never fun.
Consider the following console output, which refers to the view I mentioned that attempts to be both 360 points and 140 points wide at the same time:
2013-01-14 09:02:48.590 HelloWorld[69291:c07] Unable to simultaneously satisfy constraints. Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) ( "<NSLayoutConstraint:0x7147d40 H:[TestView:0x7147c50(360)]>", "<NSLayoutConstraint:0x7147e70 H:[TestView:0x7147c50(140)]>" ) Will attempt to recover by breaking constraint <NSLayoutConstraint:0x7147d40 H:[TestView:0x7147c50(360)]> Break on objc_exception_throw to catch this in the debugger. The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
This unsatisfiable conflict cannot be resolved except by breaking one of the constraints, which the Auto Layout system does. It arbitrarily discards one of the two size requests (in this case, the 360 size) and logs the results.
Sufficiency
Another key challenge is making sure that your rules are specific enough. An underconstrained interface (one that is insufficient or ambiguous) creates random results when faced with many possible layout solutions (see the top portion of Figure 1-3). You might request that one view lies to the right of the other, but unless you tell the system otherwise, you might end up with the left view at the top of the screen and the right view at the bottom. That one rule doesn’t say anything about vertical orientation.
Figure 1-3 Odd layout positions (top) are the hallmark of an underconstrained layout. Although these particular views are constrained to show up onscreen, their near-random layout indicates insufficient rules describing their positions. By default, views might not show up at all, especially when they are underconstrained. Chapter 4, “Visual Formats,” discusses fallback rules, which ensure that views are both visibly sized and onscreen. A sufficient layout (bottom) provides layout rules for each of its views.
A sufficient set of constraints fully expresses a view’s layout, as in the bottom portion of Figure 1-3. In this case, each view has a well-defined size and position.
Sufficiency does not mean “hard coded.” In the layout shown at the bottom of Figure 1-3, none of these positions are specified exactly. The Auto Layout rules say to place the views in a horizontal row, center-aligned vertically to each other. The first view is pinned off of the superview’s left-center. These constraints are sufficient because every view’s position can be determined from its relationships to other views.
A sufficient, or unambiguous, layout has at least two geometric rules per axis, or a minimum of four rules in all. For example, a view might have an origin and a size—as you would use with frames—to specify where it is and how big it is. But you can express much more with Auto Layout. The following sufficient rule examples define a view’s position and extent along one axis, as illustrated in Figure 1-4:
Figure 1-4 Sufficient layout requires at least two rules per axis.
- You could pin the horizontal edges (A) of a view to exact positions in its superview. (The two properties defined in this example are the view’s minimum X and maximum X positions.)
- You could match the width of one view to another subview (B) and then center it horizontally to its superview (width and center X).
- You could declare a view’s width to match its intrinsic content, such as the length of text drawn on it (C), and then pin its right (trailing) edge to the left (leading) edge of another view (width and maximum X).
- You could pin the top and bottom of a view to the superview (D) so that the view stretches vertically along with its superview (minimum Y and maximum Y).
- You could specify a view’s vertical center and its maximum extent (E) and let Auto Layout calculate the height from that offset (center Y and maximum Y).
- You could specify a view’s height and its offset from the top of the view (F) and then hang the view off the top of the superview (minimum Y and height.).
Each of these rules provides enough information along one axis to avoid ambiguity. That’s because each one represents a specific declaration about how the view fits into the overall layout.
When rules fail, they lack this exactness. For example, if you supply only the width, where should the system place the item along the X-axis? At the left? Or the right? Somewhere in the middle? Or maybe entirely offscreen? Or if you only specify a Y position, how tall should the view be? 50 points? 50,000 points? 0 points? Missing information leads to ambiguous layouts.
You often encounter ambiguity when working with inequalities, as in the top image in Figure 1-3. The rules for these views say to stay within the bounds of the superview—but where? If their minimum X value is greater than or equal to their superview’s minimum X value, what should that X value be? The rules are insufficient, and the layout is therefore ambiguous.