In the previous article, we created a simple application with nothing more than a label and a button. Despite its simplicity, the application had a few layout problems. In this tutorial, you learn how to solve those problems using Apple's layout system, Auto Layout. Let's start with a new project.
Introduction
Open Xcode and create a new project, choosing the Single View Application template. Name your application Auto Layout and set Devices to Universal. Tell Xcode where to save the project and click Create.
Remember from earlier in this series, a universal application runs on iPad and on iPhone (and on iPod Touch). This means that the user interface needs to adapt to the device it's running on. Let's explore how that works and what problems we need to solve to accomplish that.
In the project, open Main.storyboard and add five labels to the view controller's view. As you can see in the below screenshot, I positioned one label in the center of the view and one in each of its corners.
You've probably noticed that Xcode tries to help you position the labels, displaying blue, dashed guidelines when you approach the edges or the center of the view. Stick with Xcode's advice and have the labels snap into position at the edges and the center of the view.
Because we are creating a universal application, the application should look good on both iPad and iPhone. Run the application in the simulator and choose iPad Retina (or any other iPad simulator) as the destination.
Ouch. The user interface doesn't look that great on iPad. Only the top left label is positioned correctly.
Change the destination to iPhone 6 (or any other iPhone simulator) and run the application one more time.
The user interface looks even worse on iPhone. Two labels aren't even visible on iPhone. As on iPad, only the top left label is positioned correctly.
The goal of this tutorial is fixing these user interface problems with Auto Layout. Before we can apply Auto Layout, we need to know what it is and how it can help us.
What Is Auto Layout?
Auto Layout was introduced on iOS with the release of iOS 5, several years ago. Auto Layout is a layout system that is descriptive and built on constraints. This simply means that you tell the layout engine how you want the user interface to be laid out. In other words, instead of telling the layout engine that a button needs to be positioned at a specific location, you describe where it should be positioned.
Constraints are used to define or describe a layout. For example, you tell the layout engine that a button needs to be horizontally centered in its containing view. The layout engine takes a set of constraints, transforms them into equations, and sets the frames of the elements in the user interface. To make this work, you should add as many constraints to avoid any ambiguity about an element's position.
The advantage of Auto Layout is simple. Thanks to the descriptive nature of Auto Layout, the layout engine updates the user interface, regardless of the dimensions or orientation of the device your application runs on. It also makes your application future-proof. When Apple introduces a device with a new screen size, your application will automatically adjust its user interface to fit the new screen size. This isn't true if you hard-code your application's user interface.
Now that you know what Auto Layout is and how it works, it's time to explore how we can apply Auto Layout to fix the layout issues in our project.
Adding Constraints
Do you remember the blue guidelines you saw when you added the labels to the view controller's view? Those aren't the constraints you're looking for. Like I said, they are guidelines to help you position user interface elements. They are nothing more than hints Xcode gives you to stick to Apple's Human Interface Guidelines.
There are a number of ways to add constraints to a user interface element. One way is by using the Pin menu at the bottom of Interface Builder. Select the label at the top left and click the Pin menu at the bottom.
The label in the top left doesn't have any constraints yet. Let's change that by pinning the label to the top left of its parent view or superview. Using the Pin menu, we can add multiple constraints at the same time. Let's also add constraints to set the label's width and height to fixed by checking the checkboxes labeled Width and Height. This is what the Pin menu should look like. Note that the checkbox Constrain to margins is unchecked.
At the bottom of the Pin menu, the button's text now reads Add 4 Constraints. Xcode is telling us that we haven't added any constraints yet. Click the button to add the constraints we specified in the Pin menu. The label's position is now defined or described by four constraints. This is visualized in the storyboard by four, blue lines surrounding the label.
Before you continue, add constraints to the top right, bottom left, and bottom right labels. It's important that you pin each label to the nearest edge of its parent view. For example, the bottom right label needs to be pinned to the bottom edge and the right edge of the view controller's view. This is what the Pin menu should look like for the bottom right label.
With the constraints in place, run the application in the simulator. This is what you should see in the iPhone 6 simulator. That's looking much better. The center label doesn't have any constraints yet. Let's add some now.
Select the center label and add two constraints to fix the label's width and height, using the Pin menu at the bottom. Did you expect to see two blue lines? If you've correctly added the constraints, you should see red lines, not blue lines. Why is that? The red lines indicate that something's amiss. Xcode is telling us that the label doesn't have enough constraints to avoid ambiguity. The description of the label's position is currently incomplete.
We've told Xcode that the label should have a fixed width and a fixed height. What we haven't defined yet is the label's horizontal and vertical position. You can find out more about the problem by inspecting the error in the panel on the left. You should see a red arrow on the left of the View Controller Scene. Click the arrow to read the error message.
The errors are telling us that the X and Y positions are undefined. If you click the red circle next to the error, Xcode gives you the option to fix the issues by adding the missing constraints. Let's not be lazy and fix the problem ourselves.
To center the label, regardless of the dimensions and orientation of the device the application is running on, we can't use the Pin menu. Instead, we use the Align menu to the left of the Pin menu. With the label selected, open the Align menu and add two constraints to center the label in its parent view. By adding these constraints, the red lines are replaced by blue lines.
Inspecting Constraints
It's time to learn more about constraints. Constraints aren't static and they're certainly not magical. You can add, remove, and modify constraints. As an example, let's take a look at the constraints of the center label. Select the label and open the Size Inspector on the left. The Constraints section lists the constraints of to the current selection, the center label. Each constraint has an Edit button to modify the attributes of the constraint.
Click the Edit button of the constraint that read Width Equals: 42. By modifying the Constant property, you can change the label's width. Set Constant to 100 and press Enter or Return to commit the change. The size of the label in the storyboard immediately reflects the change we've made.
The Priority property of the constraint is important if multiple constraints conflict with one another. Let's illustrate this with an example. Select the center label and add a new constraint that sets the width of the label to 200. You already know how to do this. When you add the new constraint, Xcode complains that it cannot satisfy both constraints at the same time.
Select the label and open the Size Inspector to inspect the constraints. There are two constraints setting the width of the label. Click the Edit button of the constraint that says Width Equals: 100 and set Priority to High (750). The width of the label changes to 200 in the storyboard and one of the blue lines changes from solid to dashed, indicating that this constraint is overruled by a constraint with a higher priority. The dashed constraint is still active, but it's currently not being applied due to its lower priority compared to the constraint that sets the width of the label to 200.
There's another way to solve the problem. Set the priority of both constraints to Required (1000). At the same time, change = to >= for the constraint that reads Width Equals: 100. This also solves the issue. The result isn't the same, though. Both constraints are in effect, working together to define the position of the label. The constraint now defines that the label's width should be greater than or equal to 100.
Adding More Constraints
I already mentioned that there are several ways to add constraints in interface builder. Let's look at another popular technique. Open the Object Library on the right and add a text field to the view controller's view. To pin the text field to the top of the view, select the text field, press Control, and drag from the text field to the top of the view. Take a look at the below screenshot for clarification.
From the menu that pops up, select Vertical Spacing to Top Layout Guide. The top layout guide is a special guide positioned along the bottom edge of the status bar and the navigation bar. The position of the top layout guide depends on the presence or absence of a status bar and navigation bar at the top.
The text field should now be pinned to the top of the view. As expected, Xcode tells us that the text field doesn't have enough constraints to unambiguously describe its position.
Select the text field, press Control, and drag to the label on the left of the text field. From the menu, select Horizontal Spacing. Repeat this step for the label on the right of the text field. We're almost there.
To set the label's height to fixed, select the text field, press Control, and drag from the top of the text field to the bottom of the text field. Select Height from the menu that appears. This defines a constraint about the text field itself, its height.
I'm sure you agree that the basics of Auto Layout are easy to understand. It can sometimes get pretty complicated, though. What we covered so far are the basics, Auto Layout flexing its muscles. Let's finish this tutorial by looking at a very common problem that is easily solved with Auto Layout.
More Constraints
It's not uncommon that you want a subview to span the width and height of its parent view. A table view, for example, is very often the same size as its parent view. Let's see how we can handle this using Auto Layout.
Add a view from the Object Library to the view controller's view. Open the Attributes Inspector and set the view's background to blue to make sure it stands out. Run the application in an iPad simulator to see what we're starting with.
You can press Command + Left Arrow or Command + Right Arrow to rotate the device. It looks like the view maintains its width and height, but it's stuck to the top left. The goal is to have it span the width and height of its parent view. Select the blue view and add the following constraints using the Pin menu.
Before you click Add 4 Constraints, make sure that you are adding the constrains in relation to the blue view's superview. What does that mean? A constraint describes a relation between two views. You are about to apply these constraints to the blue view in relation to its nearest siblings. What we want, however, is to add constraints that define the blue view's position in relation to its containing or parent view. You specify what view the constraint defines by clicking the small triangle next to the number (which defines the constant of the constraint). Take a look at the following screenshot for clarification.
Double-check that every constraint applies to the blue view and its parent or containing view. When you're ready, add the four constraints.
Instead of blue lines, you are probably seeing orange lines with numbers. In Xcode, orange means warning. The orange lines warn you that the current position of the blue view isn't in alignment with the position of the blue view at run time. Have you noticed the orange dashed lines at the edges of the blue view's parent view? Those dashed lines symbolize the frame of the blue view if the constraints you just applied are in effect. That happens at run time.
If you don't believe me, I suggest you run the application in the iPad simulator to take a look for yourself. The blue view spans the width and height of its parent view, even if you rotate the simulator from portrait to landscape and vice versa.
The orange lines unnecessarily clutter the workspace and what you're seeing isn't what you are getting at run time. It's better to fix the issue by updating the frame of the blue view. This is simple. Select the blue view and select Update Frames from the Resolve Auto Layout Issues menu at the bottom right.
Make sure you understand what we've covered in this tutorial, because we'll be needing it in the rest of this series. In the next tutorial, we'll be discussing table views and we'll be applying some of the techniques discussed in this tutorial. To learn more about Auto Layout, browse Apple's Auto Layout Guide.
Conclusion
To say that Auto Layout is powerful is an understatement. Even though we've only scratched the surface in this tutorial, you should now have an idea what Auto Layout is and what it can do for you.
In the rest of this series, we will continue using Auto Layout. If you get stuck, make sure to revisit this tutorial or drop me a line in the comments. You can also reach out to me on Twitter.