Working with Auto Layout Programmatically
Even though Interface Builder provides a convenient visual interface for working with Auto Layout, you can also create, add, remove, and adjust constraints through code. If you add or remove views at runtime, for example, you’ll need to add constraints programmatically to ensure that your interface responds correctly to changes in size or orientation.
Creating Constraints Programmatically
You represent constraints using instances of
NSLayoutConstraint. To create constraints, you typically use
The first argument to this method, a visual format string, provides a visual representation of the layout you want to describe. This visual format language is designed to be as readable as possible; a view is represented in square brackets, and a connection between views is represented using a hyphen (or two hyphens separated by a number to represent the number of points apart the views should be). For more examples and to learn the grammar for the visual format language, see “Visual Format Language.”
As an example, you might represent the constraint between two buttons:
using the following visual format string:
A single hyphen denotes the standard Aqua space, so you can also represent the relationship like this:
The names of the views come from the
views dictionary—the keys are the names you use in the format string, and the values are the corresponding view objects. As a convenience,
NSDictionaryOfVariableBindings creates a dictionary where the keys are the same as the corresponding value’s variable name. The code to create the constraints becomes:
NSDictionary *viewsDictionary =
NSArray *constraints =
options:0 metrics:nil views:viewsDictionary];
The visual format language prefers good visualization over completeness of expressibility. Although most of the constraints that are useful in real user interfaces can be expressed using the language, some cannot. One useful constraint that cannot be expressed is a fixed aspect ratio (for example,
imageView.width = 2 * imageView.height). To create such a constraint, you can use
As an example, you could also use this method to create the earlier
[NSLayoutConstraint constraintWithItem:self.button1 attribute:NSLayoutAttributeRight
attribute:NSLayoutAttributeLeft multiplier:1.0 constant:-12.0];
To make a constraint active, you must add it to a view. The view that holds the constraint must be an ancestor of the views the constraint involves, and should usually be the closest common ancestor. (This is in the existing
NSView API sense of the word ancestor, where a view is an ancestor of itself.) The constraint is interpreted in the coordinate system of that view.
Suppose you install
[zoomableView1]-80-[zoomableView2] on the common ancestor of
The value 80 is in the coordinate system of the container, which is what it looks like if you draw the constraint.
If the bounds transform of either of the zoomable views changes, the space between them remains fixed.
If the bounds transform in the container itself is changed, the space scales, too.
NSView provides a method—
addConstraint:—to add a constraint, and methods to remove or inspect existing constraints—
constraints—as well as other related methods.
NSView also provides a method,
fittingSize, which is similar to the
sizeToFit method of
NSControl but for arbitrary views rather than for controls.
fittingSize method returns the ideal size for the view considering only those constraints installed on the receiver’s subtree together with a preference for the view to be as small as possible. The fitting size is not the “best” size for a view in a general sense—in the constraint-based system, a view’s “best” size (if you consider everything) is by definition its current size.