A few years ago you might have gotten away without Auto Layout. Now there are a couple of different iPhone sizes and on iPad you can run two apps at the same time. Auto Layout is the way to go!
This tutorial will cover the basics of Auto Layout so that you can start using it in your apps. We are going to go in depth on a few examples so that we cover all the basic types of relations you can have. In the end there are a couple of exercises to practice what you’ve learned.
What is Auto Layout?
Auto Layout is a system that makes it easy to support multiple screen sizes with one interface by making you interface react to changes. It does this by solving a set of layout constraints which describe the interface.
Why use Auto Layout?
Auto Layout makes layout code much simpler to write and maintain and most of the time you don’t event have to write code. That means less time writing code and debugging it.
It’s also easier to understand constraints than the other approaches because in your head you use words not math.
Which one do you understand faster?
rightImage.x = leftImage.x + leftImage.width + padding
vs:
rightImage
is padding
points to the right of leftImage
Another reason might be that it is the default on iOS. With only a few exceptions Auto Layout is the tool for the job.
Layout contraints
Layout constraints are descriptions of the mathematical properties and relations between views. TheNSLayoutConstraint
class is used to create constraints on both iOS and Mac. All constraints have a coefficient and a constant.
There are several types of constraints:
- size constraints – ex. an image should have a width of 200
- alignment constraints – a label should be centred vertically on the screen
- spacing constraints – space between two labels or between a view and the margin of the screen
What’s the purpose of constraints?
In the end by solving the given set of constraints, Auto Layout will determine the frame for each view in your screen. So each view should have a constraints that will help determine its width
, height
, x
and y
position. There are a few exceptions but we are going to get into them later.
Relations to parent
A view can change its size based on its superview. But that does not work the other way around. This makes you logic simpler.
One of the most common things you will do is make a view fill its parent.
Let’s have some fun!
Download the started project and open Main.storyboard
.
Find the initial view controller and add a view from the Object Library
. Resize the view so it fills the screen. You should see some helper lines when you have the correct size.
Give it a background color so you can see it. I made it red.
Ok… how will this interface look like?
Xcode has a tool for this! Open the the Assistant Editor
from the segmented control in the top right corner of Xcode.
The default will open the swift code of the view controller you where editing.
In the top of the Assistant Editor
you will find this menu. Click on Automatic.
You will see this menu.
Select Preview > Main.storyboard
to see a preview of your interface.
You can add more devices from the +
button in the lower left corner.
Add a few devices.
Now we see that our interface has a few problem on iPhone 6 and on iPad.
If you feel like you need more space when working on your interface with Preview
open, you can close the Navigator
from the control on the top right corner of Xcode.
OK. Now that you have enough room we start adding some constraints.
The easiest way to add a constraint is by control dragging. Open the Document Outline
in Interface Builder
from the button on the lower left corner.
Hold the control key ⌃
and then click drag from the red view to its superview.
You can control-drag directly on the interface. I recommended using using the
Document Outline
to have better precision. You will notice that when you do that there will be different options based on the direction you are dragging in – it will take you to vertical or horizontal properties. If you can’t click on a view because there is more than one at the same location, you can shift+control click on any point and you will see the list of views that are located at that point.
You should see a list of relations you can set.
Hold alt ⌥
to change the available constraint options. All the constraints are the same the only thing that differs is whether they are relative to the margin or not. We are going to cover margins later in this tutorial.
Add the following constraints:
- Leading Space to Container
- Trailing Space to Container
- Top Space to Container Margin
- Bottom Space to Container Margin
Now the interface should work on all devices.
You can inspect the constraints you added from the Size Inspector
.
What’s Leading/Trailing Space to Container?
It’s the space between the view and its superview. Leading
is on the left. Trailing
is on the right. In the example we did before they are all 0
.
How can I change that? What if I want to leave some empty space?
Select the leading space constraint. You can find it by selecting the view and going to the Size Inspector
. Click on the Edit
button and set the constant to 40
. You will notice that the view will resize according to the new constraint. This only happens when you edit a constraint. When you change the frame of the view you will get a layout warning or error.
Double click on the constraint and take a closer look.
Before we mentioned that constraints describe the mathematical properties and relations between views.
Can you guess what relation the Leading Space constraint describes?
View.Leading = Superview.Leading + 40
One thing we need to understand is that the constant will change its sign when we reverse the first and second item in order to maintain the same relation.
If we reverse the order we get:
Superview.Leading = View.Leading - 40
Ok. Let try another one
In the storyboard there should be another screen. Make that one the initial view controller.
Select the view controller object in the Document Outline
pane. Check the Is Initial View Controller
checkbox. The arrow should point to the other screen.
The goal is to make the the BottomContainer
stick to the bottom of the screen and fill up the width of the screen.TitleLabel
should fit inside.
Open Preview
to see how this interface will behave.
Not even close. Let’s handle the container first
Can you guess which constraints are we going to use?
Making the container stick to the bottom and fill up the width of the screen is pretty similar to making it fill the screen. Instead of adding a Top Space constraint we are going to add one for the height of the container.
First add the constraints for leading, trailing and bottom space. Remember to hold alt ⌥
to change the options.
Before we added constraints that represented relations between two views. In order to set the height of the container we only need to Pin
the value of a property.
Select BottomContainer
and click on the Pin
button. Check Height
and then add the constraint.
Notice the red borders. They signal a missing constraint or an error. After adding the last on they should be replaced by blue borders – which means that the view has enough constraints and they are satisfied. If a view is misaligned a yellow dotted frame will show the position and size indicated by its constraints.
If we look at the preview now we see that we are getting closer to our goal.
Inequalities
TitleLabel
is not completely visible on smaller screens. This is because it keeps the width it had in Interface Builder
.
Add constraints for leading, trailing, and top space.
If we look at the preview now we see we are still not there.
The label fits inside the container. But there is not enough room for the text… We don’t want all that empty space. Instead we want the trailing space between the label and container to be at least 20 points.
Constrains don’t only express equality relations. You can also say that a certain attribute should be greater than or less than a another one.
By default when you add a constraint in Interface Builder
it creates a constraint with an equality relation.
Let’s change the trailing constraint to an inequality. First select the constraint.
Go to the Attribute Inspector
and change the relation from Equal
to Greater Than or Equal
and the constant to20
. You might have the First Item
and Second Item
reversed. In that case set the relation to Less Than or Equal
and the constant to -20
or reverse the order from the First/Second Item
dropdown.
If you look at your constraint now you see that it shows a ≥
sign to indicate that that constraint is an inequality.
If we take a look at Preview
we notice that on small devices the text might be to big.
Select the TitleLabel
and from the Attributes Inspector
change the Autoshrink
attribute from Fixed Font Size
to Minimum Font Size
.
A new field should appear after you do that. Set the minimum font size to 14
.
Now the interface will display properly on all devices.
What other relations can be created?
We can add constraints on the the layout attributes of view.
- size attributes: width and height
- alignment attributes: leading, trailing, top, bottom, left, right centerX and centerY. Left and Right are the same as Leading and Trailing in left-to-right languages – in right-to-left they are reversed.
Relations to sibling
In the Document Outline
pane you should see that the screen contains more views beside the container and label. They are hidden
Make them visible by selecting them and unchecking the Hidden
checkbox from the Attributes Inspector
.
Center the heart using a center horizontally and a center vertically constraint.
You can see in preview that the heart will show in the center of the screen on all devices.
We want to make the We
and Swift
labels to be on the same level as the heart with a bit of spacing in between. We can do that! With Auto Layout you can add constraints between views that have a common superview (directly or indirectly).
Control drag from each label to the heart and add a horizontal spacing constraint to set the spacing between the label and the heart.
You will also need a align center vertically constraint to make the label be on the same level with the heart.
Now the labels will be on the same level with the heart.
Aspect Ratio
The heart logo a bit too small on an iPad, right?
We can fix that with an aspect ratio constraint.
What’s that you say?
So far when we made constraints we only used a coefficient of 1
so we ignored it to make things easier to understand. The complete relation that a constraint represents looks like this:
FirstItem.attribute1 = SecondItem.attribute2 * coefficient + constant
Control-drag from the heart to the screen and select the Aspect Ratio
constraint. Go to the Size Inspector
and double click on it.
Make sure you have Heart.width
as the first item and Superview.width
as the second one. Depending on the direction you dragged you might have gotten different combinations of width and height.
You can see that the constant for this relation is 0
and the coefficient is 1:10
or 0.1
or ten times smaller
.Interface Builder
lets you express ratios in a more human friendly way – fractions! Because when you see 0.09375
you don’t immediately think – “Oh! That’s 30/320
“. Fractions are way more intuitive.
The gray background was added to make the aspect ratio clear
As you can see this did not make the heart bigger – it only made it wider. To make it grow we need another constraint. This time from the width of the heart to its height with a ratio of 1:1
– which will make the height of the heart equal to its width. Control-drag from the heart to itself and select the Aspect Ratio
constraint.
The gray background was added to make the aspect ratio clear
To make the text be proportional with the heart we need to add two constraints for each label. One from the label to itself to so it will maintain its aspect ratio. And another one to the heart to fix the height.
One last thing to notice here. On the iPad the text won’t grow but the size of the label will. You can fix this by setting the font size to something bigger – like 100
.
Stack View
So far so good. We made the We ❤ Swift logo with an image and two labels. This approach has a small problem, you can clearly see on small devices that the logo as a whole is not actually centered inside the view.
Any idea on how to solve this?
One way would be to move the components into a container and center that container. Starting with iOS 9 we have an special container that makes this kind of layout easy to implement – UIStackView. UIStackView
uses Auto Layout to arrange its subviews one after the other horizontally or vertically . You can customise the spacing, alignment and distribution of subviews.
The alignment style refers to the way the stack view arranges its subviews perpendicular to its axis. Here are a couple of examples of alignment:
Top
Leading
Center
Bottom
Trailing
Let’t make a the main menu for a game using a stack view!
Add a few buttons for the different options from the menu and select all of them.
Find the Stack
button in the lower right corner of Interface Builder
and press it.
A stack view should wrap the buttons.
Select the stack view and open the Attribute Inspector
.
By default the spacing is 0
and the alignment is fill. Change the spacing to 10
and the alignment to Center
.
Now let’s get back to fixing the We ❤ Swift logo.
Remove the align center constraints from the heart. And the horizontal spacing ones between the heart and each label. You can do this by selecting the constraint and the hitting the delete key.
Select the labels and heart at the same time and then click on the Stack
button.
Select the stack view and set the alignment to Center
and spacing to 5
points.
Add the align center constraints to the stack view. And one more to make the width of the stack view less than the one of the screen.
Intrinsic size
You noticed that when you change the font size of a label it also changes its size. The same happens when you change the text it contains.
How does it do that?
You guessed it. The label uses some math magic to determine its size and then tells Auto Layout how big it is.
To add support for this on your own view all you need to do is override the intrinsicContentSize()
method. If you do not have an intrinsic size for any dimension you can return UIViewNoIntrinsicMetric
. The default implementation ofintrinsicContentSize()
returns UIViewNoIntrinsicMetric
for all dimensions. Some components like theUISwitch
have an intrinsic size for both dimensions, other just for one – like a text field – the height is fixed but the width isn’t.
In case you content changes you can notify Auto Layout to update your view by callinginvalidateIntrinsicContentSize()
on it.
Margins
Sometimes you want to add interface component close to the side of a container but not starting from the edge. The size of that space will depend on the screen size. On an iPad you will want to leave more space than on an iPhone. Fortunately iOS has a standard margin size defined for each device. To arrange a view relative to the margin of its container all you need to do is make the constraint relative to the margin.
The default margin on iOS is {top: 8, left: 16, bottom: 8, right: 16}
on iPhone and {top: 8, left: 20, bottom: 8, right: 20}
on iPad. To change this you need to set the layoutMargins
property in code.
container.layoutMargins = UIEdgeInsets(top: 0, left: 20, bottom: 10, right: 20)
Why are margins useful?
By using them you can adjust the placement of multiple views inside your container if they have constraints relative to its margin with one line of code.
Constraints from code
So far we only worked with Interface Builder
when adding constraints. You can do it from code but in most cases this won’t be needed.
There are three ways to create constraints from code:
- create a
NSLayoutConstraint
and set its properties - use the visual format language to create multiple constraints at once.
- use a library – my favorite is Cartography it uses operator overloading to make describing layout easy and intuitive.
I’m going to add the same constraints from code in the three different ways. The result will look like this:
NSLayoutConstraint
let leadingConstraint =
NSLayoutConstraint(item: self.redView,
attribute: NSLayoutAttribute.Leading,
relatedBy: NSLayoutRelation.Equal,
toItem: self.view,
attribute: NSLayoutAttribute.Leading,
multiplier: 1,
constant: 50)
let trailingConstraint =
NSLayoutConstraint(item: self.redView,
attribute: NSLayoutAttribute.Trailing,
relatedBy: NSLayoutRelation.Equal,
toItem: self.view,
attribute: NSLayoutAttribute.Trailing,
multiplier: 1,
constant: -50)
let topConstraint =
NSLayoutConstraint(item: self.redView,
attribute: NSLayoutAttribute.Top,
relatedBy: NSLayoutRelation.Equal,
toItem: self.view,
attribute: NSLayoutAttribute.Top,
multiplier: 1,
constant: 50)
let bottomConstraint =
NSLayoutConstraint(item: self.redView,
attribute: NSLayoutAttribute.Bottom,
relatedBy: NSLayoutRelation.Equal,
toItem: self.view,
attribute: NSLayoutAttribute.Bottom,
multiplier: 1,
constant: -50)
let constraints = [
leadingConstraint,
trailingConstraint,
topConstraint,
bottomConstraint
]
self.view.addConstraints(constraints)
Visual Format
let bindings = Dictionary(dictionaryLiteral: ("redView", self.redView))
let horizontalConstraints =
NSLayoutConstraint.constraintsWithVisualFormat(
"H:|-50-[redView]-50-|",
options: nil,
metrics: nil,
views: bindings)
self.view.addConstraints(horizontalConstraints)
let verticalConstraints =
NSLayoutConstraint.constraintsWithVisualFormat(
"V:|-50-[redView]-50-|",
options: nil,
metrics: nil,
views: bindings)
self.view.addConstraints(verticalConstraints)
For more about Visual Format language read this tutorial.
Cartography
layout(redView) { view in
view.edges == inset(view.superview!.edges, 50, 50, 50, 50)
}
Useful Shortcuts
control-drag from a any view to add a constraint on it. After you click a menu will appear. If you press alt the options will change – making them relative or not the the margin
⌘
+ ⌥
+ =
: rearranges all the selected views based on their constraints or all views in the current screen is none are selected.
⌃
+ ⇧
+ click
: shows you a list of all the objects that are under the cursor in Interface Builder
.
Conclusion
Auto Layout is a powerful tool – and the default layouting tool for iOS. Although most cases can be solved directly from Interface Builder it’s a good idea to understand what happens under the hood.
Exercises
All exercises will give you an preview of an interface in a couple of devices. You task will be to recreate them based on the instructions.
1) The red view should have a width equal to 2/3
of the width of the screen and half the height. The blue view should have a 1/3
of the width of the screen. And the purple view should have half the height and the same width as the screen.
2) This is the view for a random cute image viewer. It has an image view that fills most of the screen and three buttons.
3) Under the navigation bar there should be a container. There should be a segmented control centered inside that container. The rest of the screen is taken by a table view.
If you found this tutorial useful please take a moment and share it with your friends!
The best post on the web about Auto Layout so far. Thank you so much!
Another great tutorial! Thanks.
Weirdly enough I was looking just for this kind of tutorial when I got the email alert O_o
Thank you so much … this is grea. ?
You really out did yourself on this one guys, absolutely the most extensive and comprehensive Auto Layout guide on the net. If you’re still coding layouts by hand, or using Autoresizing Masks, stop right now, read this guide, and you’ll be happy that you did!
Thank you!
Could you guys maybe do a Metal fragment/pixel shader tutorial?
Awesome. Really useful.
Absolutely amazing! Thank you so much! I think it would be helpful to add an exercise onto the centering the label “auto layout 101”. That took me a surprisingly long time to understand, but it makes everything easier now.
This is a very good tutorial. I always do my project programmatically unfortunately I can’t find any tutorial how to do autolayout using swift. All i see is tutorials using storyboards. Is this the standard way of developing app in ios (storyboard)? Sorry I’m just new in this environment.
Use storyboards! If you can’t stand them then use nibs. If you don’t like nibs – stop making iOS apps :p
Awesome One!!!!!
Great article! Will you consider going into more depth on programmatic constraints from code? I think that would be really helpful!
Thanks again:)
Really Great article on Autolayout. Thanks
It’s really useful for me… thanks
a great tutorial !! ?
Hi Andrei,
Excellent examples and exercises!
Thanks and I learned a lot!