Auto Layout Basics
This guide provides a quick overview and basic examples of the most
common uses cases for using Auto Layout in Interface Builder. You can also check out Apple’s
official Auto Layout documentation.
What is Auto Layout and why use it?
In the first few years of iOS, every iPhone screen had the same
point dimensions of 320x480 width x height, and it was
often possible to describe an app’s layout by specifying the absolute
position and size of views. Screen sizes have diversified ever since:
the original iPad (2010) introduced a 768x1024-point display, the iPhone
5 (2012) stretched the iPhone screen to 320x568 points, and today’s
lineup spans many more sizes.
These days, most applications will want their layout to be responsive to
changes in the screen size or the content they are displaying. Auto Layout
provides a convenient way for you to describe how the size and position
of your views should change when the size and position of
their parent views or neighboring views change. This can happen for
example when:
- your application is run on different devices
- the size of or number of neighboring views change to reflect a change
in the content the application is displaying - the user rotates the orientation of the device
In Auto Layout you describe a view’s layout by providing one or more
constraints that describe how its size and position are related to
the size and position of other views.
Example of layout problem
To get a sense of the kind of problem that arises when laying out views
and that Auto Layout can solve for us, consider the following example.
Using Interface Builder we’ve added a single view (colored red) inside
the view controller’s root view.
Without adding any layout constraints here is what this interface looks
likes in some situations where this app might run.
Depending on the use case, chances are this is not behavior that we
want. For example, if the red view were meant to be a button that is
always pinned to the upper right corner of the screen, the behavior would not
be consistent for landscape orientations.
Basic constraints
In Auto Layout, you describe your application’s layout by adding
constraints that define relationships between the size and position of
its views. In order for Auto Layout to function properly you’ll need to
provide enough constraints for each view so that the system can
determine its size (width and height) and location (the x and y
coordinates of the top left corner of the view).
You can add constraints in Interface Builder by selecting one or more
views and using the Auto Layout controls (boxed in red below). The
buttons are from left to right:
- Update Frames - When adding constraints, this will apply the change to the view frames.
- Align tool - Use to align view in relation to the main container, or to each other.
- Add New Constraints- The main interface for adding new constraints. Note that if you want to edit existing constraints, you must do this from the Size inspector. Adding constraints on views you already worked on will simply add more constraints, leading to conflicts.
- Resolve Auto Layout Issues - Use this as a last resort to let Xcode try its best to figure out how to handle constraints.
- Embed in - Used to Embed a view in another view or a Navigation or Tab Controller.
You can also add constraints by control-dragging from one view to the
relevant area of another view and selecting the appropriate item in the
context menu.
Here are some of the most basic situations you’ll come across.
Pinning to one or more edges
You’ll often find yourself wanting to position a view to be a fixed
distance from an edge of its parent view or one of its neighboring
views.
Here we pin the red view to the top right corner of its parent view.
This might be useful for example if the red view is a menu button that
we want to always be accessible from the same location. Notice that we
had to specify the height and width of the view as well so that Auto
Layout would have enough information to figure out both the location and
size of the red view.
The result when we run our app is as follows.
Resize with parent view
Another common situation is for a view to resize its dimensions (either
height, width or both) to match the parent view’s dimensions.
Here we specify that the red view should be pinned to the top of the
screen with fixed height, but it should resize to span the width of the
screen. This might be useful for example in a situation where the red
view contains an alert message.
Here’s what the result is when running our app:
Center a view within a parent view
Sometimes you’ll want to center a view (either vertically, horizontally,
or both) within another view.
Here we center the red view vertically on the screen. This might be
useful if we need to show our logo in the Launch Screen.
Working with constraints in Interface Builder
Here are a few common situations that will come up as you add and modify
constraints in Interface Builder
Specifying the second view to which a constraint should be relative
The pin tool by default will try to create a constraint relative to the
nearest neighbor. You can change which view a constraint is relative to
by clicking on the small arrow and selecting the right view in the drop
down menu.
Editing constraints
Note that constraints added using the align and pin buttons are
additive. They do not update existing constraints, but rather create
entirely new ones. For example, here we try to set a second height
constraint which results in a case of conflicting
constraints.
You can edit a constraint by selecting the view associated with that
constraint and using the Size inspector, or simply selecting the
constraint directly in the Scene Outline.
Here we update our red view to have a different height and different
inset distance from its parent view.
Constraint Errors and Warnings
The Auto Layout system will give an error if it is unable to determine
the correct position and size of any of the views in your scene. It
will provide you with a warning when there is an issue that may result
in unexpected behavior—for example something that would result in
your interface not looking like it appears in interface builder.
Misplaced Views
As you edit your constraints you’ll run into situations where the
position and/or size of your views as they appear in Interface Builder
no longer match what would be the result of the constraints you’ve
created. In this case Auto Layout will give you a “Misplaced
Views” warning.
Update frames
One way to fix this warning is to update the views’ sizes and locations
in the Interface Builder to match the constraints. You can do this by
selecting “Update frames” from the issues button or in the Auto Layout
error inspector. You should use this option when you know that your
constraints are correct, and the way the views are laid out on the
canvas is wrong. You should not select this option if you suspect one
of the constraints is wrong. In particular, do not select this option
if Interface Builder says that you have “Missing Constraints” as this
will result in confusing placement of your view off screen or having it
be sized to zero.
Here we edit the red view’s position (which could happen if you accidentally move it). After selecting “Update Frames” the view returns to its defined position.
Update Constraints
Other times you’ll be editing a view’s location or size independently of
manipulating constraints, and the view’s location on the canvas is the
location you want to keep. You can update existing constraints to match
the view’s location on the canvas by selecting “Update Constraints” from
the issues button or in the Auto Layout error inspector.
After “updating constraints”, you should check to see if the system
modified your constraints in a sensible way since sometimes
constraints will be updated in a way you did not intend. In particular,
do not use this option if Interface Builder tells you that you have
“Conflicting Constraints” since this will update all constraints to fit
the location of the view on the canvas, and you will end up having
duplicate or redundant constraints.
Here, we’ve modified the x position and height of the red view. Selecting “Update
Constraints” changes the space constraints between the red
view and the parent view to match the new layout.
Conflicting Constraints
If you create constraints such that Auto Layout cannot simultaneously
satisfy all of your constraints (i.e. your system is overconstrained),
then it will give you an error about “Conflicting Constraints”. You’ll
have to remove at least one constraint to resolve the issue. Sometimes
it’s helpful to remove all constraints for a particular view and start
over. You can do this by selecting the view and choosing “Clear
Constraints” from the issues button.
Missing Constraints
If you do not provide enough information for Auto Layout to determine
both the x-coordinate and y-coordinate of the top left corner of your
view and the width and height of your view, then it will give you an
error about “Missing Constraints”. This can be resolved by adding an
appropriate constraint. Be careful when using the automatic issue
resolver since this may not add the constraint you expected or it may
add a relatively unintuitive constraint.
It’s important to understand what Xcode needs to know about your view in order to place it with Auto Layouts:
- The X and Y position of the top left corner of the view’s frame.
- Its width, which can be either set as constraint, or dynamic based on the spacing to the left and right.
- Its height, which can be set, or also based on top and bottom spacing.
Dealing with flexible content size
So far we have been only dealing with views whose content (and hence
size) does not change during run time. However, many views that we end
up working with will have dynamic size depending on their content. The
most prominent examples are UILabels,
UIButtons, and UIImageViews.
Intrinsic content size of a view
Views that can determine the size they “should be” have what is
known as an intrinsic content size. This size
is the size the view has determined would be best to display its current
content. For example a UILabel’s intrinsic content size will change
depending on the text in the label, and a UIImageView’s intrinsic content
size will depend on the image it has loaded.
When using Auto Layout, you do not necessarily have to provide width and
height constraints for views with an intrinsic content size since the
system will take this into account when computing the final layout of
the views. To get a sense of how intrinsic content size works we can
consider some examples.
Two labels: one on top of another
Here we add constraints for two labels that are located vertically adjacent
to each other. We pin the first label to the top left corner, specify
the vertical space between the labels, pin the second label to the left
margin and also give both labels a width. Notice that we did not have
to specify the height for either label.
Now we make one of the labels a multi-line label by setting its “Lines”
property to 0 in the attributes inspector—this means that the
label can have an arbitrary number of lines. Once we add sufficiently
long text and update frames, the first label changes its height to match
the content and the second label gets automatically pushed lower down
the canvas so that the vertical space between the two labels is
maintained. We had to update the frames manually here, but this would
be done automatically for us at runtime when we change the content of
the label.
Auto Layout offers some suggestions as warnings, and you can choose the right one depending on the desired behavior or use case.
Left aligned label next to right aligned label
Consider another example where we have two single-line labels
horizontally adjacent to each other. We want to pin one label to the
left margin and the other to the right margin—this is common for
example in the design of many table view cells.
Inequality constraints
We also want to specify that the labels should have a minimum amount of
horizontal space between them so that they do not run into each other.
We do not know the exact amount of horizontal space since the contents
of the labels might change at run time. One way to accomplish this is
to define an inequality constraint where we can specify that a certain
constraint’s value be greater than or less than a constant.
Compression resistance
In this same example, what if the text in our labels becomes long enough
so overlap is unavoidable? As seen below, at least one of the labels
will start to shrink and compress its content (in this case by using
an ellipsis).
How do we control this shrinking behavior? Each view has a horizontal
and vertical content compression resistance priority that can be
modified. Higher compression resistance means the view is less likely
to shrink its content.
In this case we specify that the green label is the one whose content
should be compressed if there is a conflict by lowering its compression
resistance priority.
Content hugging
Sometimes you’ll want views to be a fixed distance from each other
and for one of the views to expand to fill the available
space — this is common for example with buttons. This can be
accomplished by pinning the views to the surrounding views and adding a
fixed constraint for the space between them. You can specify which view
should fill the available space by changing the content hugging
priority of a view. A lower content hugging priority means the view is
more likely to expand to match constraints, whereas a higher content
hugging priority means a view wants to be as close to its intrinsic
content size as possible.
Manipulating constraints programmatically
Everything Interface Builder does with constraints can also be done in
code. The most common reasons to work with constraints programmatically
are laying out views you create in code and animating a layout by
changing a constraint’s constant at runtime.
Two things to know before creating constraints in code:
- Set
translatesAutoresizingMaskIntoConstraints
tofalseon any view you lay out with your own constraints. Views
you create in code have this property set totrueby default, which
makes the system generate constraints from the view’s frame — those
generated constraints will conflict with the ones you add. (Views
you add in Interface Builder getfalseautomatically.) - Constraints only work once the views share a common ancestor, so add
the view to its superview before activating constraints.
The preferred API is the layout anchor
API: each view exposes anchors (topAnchor, leadingAnchor,
widthAnchor, centerXAnchor, …) whose constraint(...) methods
build NSLayoutConstraint
objects. Activate them in a batch with
NSLayoutConstraint.activate(_:).
For example, this pins a view to the top-right corner of the safe area
with a fixed size — the programmatic equivalent of the “Pinning to one
or more edges” example above:
let redView = UIView()
redView.backgroundColor = .red
redView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(redView)
NSLayoutConstraint.activate([
redView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
redView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -20),
redView.widthAnchor.constraint(equalToConstant: 100),
redView.heightAnchor.constraint(equalToConstant: 100)
])
To modify a constraint later (for example to animate a view to a new
position), keep a reference to it in a property and change its
constant, then call layoutIfNeeded() inside an animation block.
You can also connect an @IBOutlet to a constraint created in
Interface Builder and adjust its constant the same way.
Visual Format Language (VFL)
The Visual Format Language is an older, string-based way to define Auto Layout constraints for views.
As with anchors, ensure translatesAutoresizingMaskIntoConstraints is set to false.
func addConstraints() {
//Collect Views to apply VFL
let buttonsDictionary = ["button1": flagButton1,
"button2": flagButton2,
"button3": flagButton3]
//Metrics establish Fixed Constants
let metrics = ["topSpacing": 80, "bottomSpacing": 20, "buttonHeight": 20, "buttonSpacing": 20]
//Note that priorities can be set using @. 1000 for Required. < 100 for Optional. Example: @999
//Horizontal constraints
for buttonName in buttonsDictionary.keys {
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-[\(buttonName)]-|", options: [], metrics: nil, views: buttonsDictionary))
}
//Vertical constraints
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-(==topSpacing)-[button1(>=buttonHeight@997)]-(==buttonSpacing@999)-[button2(==button1)]-(==buttonSpacing@999)-[button3(==button1)]-(>=bottomSpacing@998)-|", options: [], metrics: metrics, views: buttonsDictionary))
}
Understanding debug output
When Auto Layout cannot satisfy all of your active constraints at
runtime, it does not crash. Instead it prints a report to the console
that starts with:
Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
followed by the list of conflicting NSLayoutConstraint objects, and a
line beginning Will attempt to recover by breaking constraint — the
system then discards one of the constraints so it can continue, which is
why an unsatisfiable layout usually shows up as one view being the wrong
size or in the wrong place rather than as a crash.
Tips for reading the dump:
- Each constraint is printed in an equation-like form, e.g.
<NSLayoutConstraint:0x... UIView:0x....height == 100>. Views appear
as class names plus memory addresses, which makes big dumps hard to
read. You can give a constraint a name by setting its
identifier
property (also editable in the constraint’s Attributes inspector in
Interface Builder);
the identifier is then printed in the log in place of the address soup. - A constraint of class
NSAutoresizingMaskLayoutConstraintin the list
is a strong hint that you forgot to set
translatesAutoresizingMaskIntoConstraints = falseon a view you are
constraining in code — the conflicting constraints were generated from
the view’s frame, not added by you. - The last line of the report suggests making a symbolic breakpoint at
UIViewAlertForUnsatisfiableConstraints. Add one via Breakpoint
Navigator → + → Symbolic Breakpoint with that symbol name, and the
debugger will pause at the moment the conflict happens so you can
inspect the views involved instead of reading addresses out of a log
after the fact.
The other class of layout problem, ambiguity (too few constraints —
the runtime counterpart of Interface Builder’s “Missing Constraints”),
produces no console output at all; the system silently picks one of the
possible layouts. While paused in the debugger you can check
hasAmbiguousLayout
on a view and call
constraintsAffectingLayout(for:)
(e.g. po view.constraintsAffectingLayout(for: .vertical) in the LLDB
console) to see which constraints the layout engine is actually using
for an axis.
Other topics
Springs and Struts and AutoResizing Mask
Before Auto Layout, iOS views were laid out with the “springs and struts”
model: each view carried an autoresizing mask describing which margins
stayed fixed (struts) and which dimensions stretched (springs) when its
superview’s bounds changed. That model still exists in UIKit as the
autoresizingMask
property — when a view’s bounds change, it resizes its subviews according
to each subview’s mask, and the default (empty) mask means the subview is
not resized at all.
The main place this legacy model intersects with Auto Layout today is the
translatesAutoresizingMaskIntoConstraints
property. For views you create in code, the system sets it to true and
generates constraints that duplicate the autoresizing mask’s behavior —
those generated constraints fully specify the view’s size and position, so
you can’t add your own constraints to modify that size or position without
introducing conflicts. Set the property to false
on a programmatically created view before adding your own constraints.
Views you add in Interface Builder don’t need this: the system sets the
property to false for them automatically.


















