Constraints
Constraints are rules that allow you to describe view layout. They limit how things relate to each other, specifying how they may 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 visual 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 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-designed interface consists of constraints that are satisfiable and sufficient. The next sections introduce these terms and explain why they are important.
Satisfiability
Cocoa/Cocoa Touch takes charge of meeting layout demands via a constraint satisfaction system. The rules must make sense. In logic systems, this is called satisfiability or validity. A view cannot be both to the left and the right of another view. So, the key challenge when working with constraints is ensuring that the rules are rigorously consistent.
Any views you lay out in Interface Builder are guaranteed to be satisfiable. You cannot create a wrong interface with inconsistent rules in IB. The same 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. Xcode provides you with verbose updates explaining what might have gone wrong. In some cases, your code will raise exceptions. Your app terminates if you haven’t implemented handlers. In others (as in the example that follows), the Auto Layout keeps your app running by deleting conflicting constraint rules on your behalf. This produces interfaces that can be somewhat random.
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 not fun.
Consider the following console output. The text refers to that view I mentioned that attempts to be both 360 points and 140 points wide at the same time. The bolding in the text is mine. I’ve highlighted the sizes for each constraint, plus the reason for the error. In this example, both rules have the same priority and are inconsistent with each other:
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) can create random results when faced with many possible layout solutions (see Figure 1-5, top). 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-5. 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 may not show up at all, especially when they are underconstrained. Chapter 4, “Visual Formats,” discusses fallback rules that ensure 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 Figure 1-5 (bottom). In this image, each view has a well-defined size and position.
Sufficiency does not mean “hard coded.” In this example, none of these positions are exactly specified. 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 parent’s left-center. These constraints are sufficient because every view’s position can be determined from its relations to other views.
A sufficient or unambiguous layout generally offers 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 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, illustrated in Figure 1-6:
- 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 parent (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 parent (D) so that the view stretches vertically along with its parent (minimum Y and maximum Y).
- You could specify a view’s vertical center and its maximum extent (E), letting 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), hanging the view off the top of the parent (minimum Y and height.).
Figure 1-6. Examples of sufficient layout involve two rules per axis.
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 of Figure 1-5. The rules for these views say to stay within the bounds of the parent, but where? If their minimum X value is greater than or equal to their parent’s minimum X value, what should that X value be? The rules are insufficient, and the layout is ambiguous.