The introduction of Adaptive Layout caused a huge paradigm shift for iOS app designers. By using it, you can now create a single layout for your app which works on all current iOS devices — without crufty, platform-specific code!
This tutorial serves as your introduction to Adaptive Layout. You’ll learn about universal storyboards, size classes, and layout and font customizations as well as the improved Interface Builder, which will help you along the way.
You’ll create the user interface for a simple weather app, and you’ll build it completely from scratch. If you’re not a fan of Auto Layout, don’t worry; the first part of this tutorial provides you with a gentle, step-by-step approach to building an interface using Auto Layout.
Amaze yourself at how much you can accomplish without writing a single line of code!
Use the Download Materials button at the top or bottom of this tutorial to download all of the files you’ll need for this tutorial.
Universal Storyboards are the first step on your journey towards Adaptive Layout, because they allow you to use the same storyboard for both iPads and iPhones. There’s no need to keep per-device storyboards in sync with each other, which is a monotonous process that is fraught with error.
To get started, open Xcode and select File ▸ New ▸ Project….
Select iOS ▸ Application ▸ Single View App, then click Next:
Set Product Name to AdaptiveWeather and the Language to Swift. Make sure the checkboxes are all unchecked, then click Next:
Once you’ve specified the location for your project, take a look at the Project navigator and you’ll see the following files:
Main.storyboard is the single storyboard for all devices, no matter their screen size. Open the storyboard and you’ll see that it contains a single view controller, currently the size of an iPhone 8’s screen:
The Use Trait Variations option, which you can find in the File inspector, enables this new format for your project. Select the storyboard and open the File inspector and you’ll see the checkbox option as shown below:
This is the default option for all new iOS projects.
Use Safe Area Layout Guides is the default setting, because it supports the notch area of mobile phones. You can turn on this option yourself when you upgrade your old projects.
Setting up Your Storyboard
Now, you’re going to get your storyboard ready to use. To start, open Main.storyboard and drag an image view from the Object Library onto the view controller canvas.
In the Size inspector, set the X position to 37 and the Y position to 20. Set the Width to 300 and the Height to 265.
Next, drag a View from the Object Library and place it below the image view.
In the Size inspector, set the X position to 37 and the Y position to 340. Set the Width to 300 and the Height to 265:
Now, select the view you just added, open the Identity inspector and enter TextContainer in the Label field of the Document pane. If the Document pane is not visible, press the Show button to reveal it. Here, you can give the view a name, which makes it easier to see in the Document inspector. This view will eventually hold the city and temperature labels of your app.
It’s often hard to see views after dragging them in from the Object Library because their default background color is white and so is the view controller’s view. To fix this, select the view controller’s view, open the Attributes inspector and set its background color to #4AABF7.
Next, select the TextContainer view and set its background color to #3780BA.
Your view controller should now look like the screenshot below:
These two views are the only direct descendants of the view controller’s view. Your next task is to give them some layout constraints.
Select the image view and press the Align button in the Auto Layout toolbar. Check the Horizontally in Container checkbox, make sure the value is set to 0 and click Add 1 Constraint.
Then, click the Add New Constraints button and add a top spacing of 20 to the superview constraint, like so:
Click Add 1 Constraint.
The constraints you added above ensure that the image view has a fixed-size margin from the top and centers the view left to right.
You now need to configure the space between the image view and the text container view. Control-drag from the image view down to the text container view, like so:
This displays the constraint context menu again. Select Vertical Spacing:
This constraint determines the amount of vertical space between the bottom of the image view and the top of the TextContainer view.
Select your image view and open the Size inspector to see how it looks now:
You’ll see the three constraints you just added to your layout. You can configure each constraint from within the Size inspector.
Press the Edit button in the Bottom Space To: TextContainer constraint, then configure the constraint properties in the dialog. Set Constant equal to 20:
Click away from the dialog to close it.
Adding More Constraints
You’ve now configured your TextContainer view to have a gap of 20 points from the bottom edge of the image view, but you also need to add constraints to the other three sides of the view.
Select your TextContainer view, then click the Add New Constraints button in the bottom bar to show the dialog.
In the Spacing to nearest neighbor section, set the left, right and bottom spacing in the superview to 0. Ensure that you have unchecked Constrain to margins; this removes padding from around your view.
For reference, the dialog should now look like this:
Click Add 3 Constraints to add the new constraints to your view. This pins the text container view to the left, right and bottom edges of the view controller’s view.
Your storyboard should now look like the screenshot below:
Adding Content to Your View
You’ll notice a few orange and red constraints on your view; this indicates there are issues with these constraints that need your attention.
You could have the storyboard automatically update the frames of the contained views to make these issues disappear, but if you do this right now, your image view will shrink to size zero.
That’s because your image view doesn’t have any content yet, which means it has an intrinsic height and width of zero. Auto Layout relies on a view’s intrinsic size to determine its width and height if physical width or height constraints are not supplied.
Instead, you’ll add some images to your app.
In the Project navigator, open Assets.xcassets. If you haven’t already, use the Download Materials button at the top or bottom of this tutorial to download the image files.
Inside the AdaptiveWeather folder is a folder named cloud_images. Inside, you’ll find three files. Select all three of them in Finder, and drag them to the empty pane on the right-hand side of the asset catalog:
This creates a new image set and assigns the three images appropriately:
You can now use your image set to populate your image view.
Head back to Main.storyboard and select your image view.
Switch to the Attributes inspector, type cloud_small into the Image field and select Aspect Fit in the Content Mode drop down list, like so:
Your storyboard should now look like this:
There’s your image, and everything seems to be in place. The view controller automatically rearranged the views to match the new constraints.
Normally, you’d now build and run your project on each of the different simulator versions — in both orientations — to test this new universal storyboard. That process is laborious at best; fortunately, Xcode gives you a better option with its trait variation previews.
To see how they work, open Main.storyboard, find the View as button at the bottom of the canvas and click on it. This expands the Trait chooser menu:
In the Devices section, select iPhone 4s, which is the right-most icon shown in the Device area.
You’ll notice that the canvas has switched to a different configuration: Your views are now on a four-inch iPhone screen, as shown below:
To view your layout in landscape mode, select Landscape in the Orientation area of the Trait chooser:
This is a huge improvement over firing up multiple simulators! With the click of a button, you can check if your layout still works on a different device.
Notice anything odd about the landscape iPhone preview? That’s right — the cloud image is far too big. To fix this, you’ll add a new constraint to the image view.
Constraining the Image to the Right Size
Head back to the storyboard. Control-drag from the image view to the view controller’s view to create a new constraint. From the context menu, select Equal Heights:
Several constraints on the storyboard are now colored red. This is because the constraint you added conflicts with the existing constraints. The image view can’t have the same height as the view controller’s view and still maintain the vertical margins you created earlier.
To fix the conflicts, select the constraint you added in the Document Outline and open the Attributes inspector. If the First Item is not cloud_small.Height, then select Reverse First and Second Item in the First Item drop-down as shown below:
Next, set the Relation to Less Than or Equal, then set Multiplier to 0.4 as shown below:
This sets the cloud image to either the intrinsic size of the image or 40% of the height of the screen, whichever is smaller.
You’ll notice that the canvas automatically refreshes as soon as you update the constraint, as demonstrated below:
Since this is a weather app, you’ll now add some labels to show the city name and the current temperature.
Adding Content to the TextContainer
In Main.storyboard, switch back to the Portrait iPhone 8 trait and drag two labels from the Object Library onto the TextContainer view. Then arrange them roughly as shown below:
Select the top label and use the Align and Add New Constraints menus to center the label horizontally.
Add a top spacing to nearest neighbor of 10, as shown below:
Next, select the Attributes inspector and set Text to Cupertino, Color to White Color, and the font to System, Thin with a Size of 150.
You’ve probably noticed the text is illegible. This is due to the label’s frame; you’ll resolve this problem shortly.
Now, select the other label, again use the Align and Pin menus to center it horizontally. Set a bottom space to nearest neighbor of 10 and check that the Size inspector matches the following:
Use the Attributes inspector to set the Text to 28C, the color to White Color, and the font to System, Thin with a size of 250.
The labels reach out of bounds and overlap in the storyboard, which isn’t the look you’re going for. However, take a look at a different trait before you fix anything; the iPad Pro 9.7″ version is looking pretty good:
Whereas, predictably, the font size is far too big for the iPhone:
You’ll correct these sizing issues in the next section of this Adaptive Layout tutorial.
Universal storyboards are great, but you’ve already discovered that creating one single layout for all displays is a bit of a challenge. However, Adaptive Layout has a few more tools and tricks to solve these issues.
Size classes are one of the core concepts behind Adaptive Layout. You can use the size class property to represent the amount of content that you’ll display in a given horizontal or vertical dimension in any view or view controller.
Xcode provides two size classes: Regular and Compact. Although they affect the physical dimensions of a view, they also represent the semantic size of the view.
The following table shows how the size classes apply to different devices and orientations:
These are the size classes that the device passes to the app. However, you can override these size classes at any point in the view hierarchy. This is useful when using a view controller in a container which is significantly smaller than the screen.
Size Classes and You
What does that mean for you and the design of your app? Although your app is aware of size classes, the layout you’ve built is size class agnostic — that is, your layout remains the same for all size classes.
This is an important point when it comes to the design phase of your adaptive layout. You should build a base layout first and then customize each specific size class based on its individual needs.
Don’t treat each of the size classes as a completely separate design. Think of an adaptive layout as a hierarchy, where you put all of the shared design into the parent and then make any necessary changes in the child size classes.
In this tutorial, there’s been almost no mention of configuring layouts for specific devices so far. That’s because a core concept of an adaptive layout is that you can differentiate size classes from device-specific characteristics. You can use a view that supports adaptive layout in a full-screen view controller as well as in a container view controller, and it will appear differently in each.
This also benefits Apple, as it can expand the range and characteristics of its devices without forcing developers and designers to re-engineer their apps.
You’ll use size classes to customize the landscape layout for iPhone, since the current layout doesn’t cope well with vertical restrictions.
Working With Size Classes
Now, it’s time to introduce trait variations to your app. To start, select a Compact Height configuration such as an an iPhone SE in landscape, then click Vary for Traits to the right of the Trait chooser menu.
Here, you can choose a size class to customize and you can introduce variations based on width and height:
Your current layout doesn’t work properly for devices with compact heights. To fix this, choose Height in the Vary for Traits menu:
You’ll notice that the bottom bar immediately turns a fetching shade of blue. This indicates that you’re now working on a size-class-specific layout.
Customizing the Constraints for Different Size Classes
In order to change the layout, you’ll need to temporarily change a few constraints. In Auto Layout terminology, this is known as installing and uninstalling constraints. An installed constraint is currently active, whereas an uninstalled constraint is not active within the current size class.
Click the image view to select it, then open the Size inspector. You’ll see a summary of the constraints which affect this view:
Select the Align Center X to: Superview constraint by single-clicking it, then press the Delete key on the keyboard to uninstall the constraint for the current size class.
The constraint immediately disappears from the storyboard view and becomes grayed out in both the Document Outline and the view’s Size inspector:
Double-click on the uninstalled constraint in the Size inspector to select the constraint. You’ll see an additional line at the bottom as shown below:
This indicates that you’ve installed the constraint for the base layout, but not for the Compact Height layout — that is, the one you’re currently editing.
Repeat the same procedure to uninstall the other three constraints associated with the image view. When you’re done, your document outline and image view’s Size inspector should resemble the following:
Now you can add the constraints required for this size class. Use the Align and Pin menus to Vertically Center in the Container:
Set left spacing to nearest neighbor to 10:
Control-drag from the image view to the view controller’s view and then select Equal Widths in the popup menu.
Open the Size inspector for the image view and double-click the Equal Width to: Superview constraint to reveal its properties.
If the First Item isn’t cloud_small.Width, use the drop down menu to Reverse First and Second Item. Now, update the Multiplier to equal 0.45.
The constraints for the image view are now set up correctly for all size classes, but the text container still needs a bit of attention. You’ll need to alter the constraints for this size class to move the labels to the right-hand side.
Positioning the Labels
The TextContainer view has internal constraints to position the labels, which work just fine. However, the three external constraints, which pin the TextContainer to the left, right and bottom sides of the view, aren’t working properly right now.
To pin the view to the bottom right-hand side of the parent view, you’ll need to uninstall the left-hand constraint.
Select TextContainer in the document outline and uninstall the Leading Space constraint in the size inspector. To verify that you have uninstalled the constraint for compact heights, select it in the document outline, and check the Size inspector:
You now need to add two constraints to your TextContainer to position it correctly. The view should be half the width of its superview and pinned to the top.
In theory, you can simply Control-drag from the TextContainer view to the view controller’s Safe Area, as you’ve been doing all along. However, in practice, it’s often difficult to grab the view when content is in the way. It’s much easier to use the document outline to do your dirty work.
Control-drag from TextContainer in the document outline up to the view controller’s Safe Area.
Shift-click on Vertical Spacing and Equal Widths to create the new constraints:
Open the Size inspector for your TextContainer and update the two new constraints you’ve just created as follows:
- Top Space to: set Constant of 0 and change Safe Area.Bottom to Safe Area.Top.
- Equal Width to: set Multiplier to 0.5. Note that you might have to switch the first and second items in this constraint, as you have done before. Simply double-click on the constraint and choose Reverse First and Second Item.
The storyboard will update and display the new layout:
The layout has changed completely now, and you’re closer to the finished product. There are still a few issues to correct with the font sizes, which you’ll address in the next section.
The current font sizes in your TextContainer look pretty good in the iPad view using a container with regular size classes, but the font size is too large for compact size classes. Fear not — you can also override font sizes within your size classes!
Click Done Varying at the right of the Trait chooser menu. The bottom bar of your view turns gray to indicate that you’re back to the base layout.
Select the Cupertino text label and open the Attributes inspector. Click the small + to the left of the Font:
This reveals a menu for selecting the size class combination that you’ll use to override the font size. Select Compact for the Width and Any for the Height like so:
This creates a second font selector box that applies to the specified size class combination. Update the font size in the new selector box to 90:
Now, select the temperature text label and repeat the same process. This time, set a font size override of 150 for Compact Width, Any Height.
Interface Builder updates automatically to show the effect of the changes you made:
Well, it’s looking a little better, but the view cuts off the Cupertino label. Fiddling with the font sizes until the label fits perfectly isn’t a particularly scalable option. Cupertino is quite a long place name, but Washington, D.C. is longer — and Kleinfeltersville, PA is longer still! What’s a designer to do?
Using Auto Layout to Resize the Fonts
Once again, Auto Layout comes to the rescue! You simply need to limit the width of the two text labels to match the width of the TextContainer. Control-drag from the Cupertino label to the TextContainer, and select Equal Widths.
Repeat the process with the temperature label. The canvas updates to show the effects of your changes as follows:
Hmm, having the text truncate is not exactly what you want. This is the default behavior for labels that contain more text than will fit in the available space; however, there’s also an option to adjust the font size automatically to fit the content.
Select the Cupertino label and open the Attributes inspector. Change the AutoShrink drop-down to Minimum Font Scale and ensure that it’s set to 0.5.
Also, update the Text Alignment to Centered. Your Attributes inspector should now look like this:
Repeat the same procedure for the temperature label.
Take a look at the Interface Builder canvas; the iPhone layouts look much better now:
Working with the preview editor is great, but it’s a good idea to check that everything is still working correctly, so build and run your project. The iPhone screens look like everything is sizing correctly:
Safe Area Guides
In iOS 11, Apple replaced top and bottom layout guides with a single Safe Area Guide. This helped with the introduction of the iPhone X, which has the notch and the bottom indicator view.
By default on new projects, Interface Builder will choose the Safe Area Guide. For this example, that’s not the expected behavior you want.
Open the Size inspector for your TextContainer and update two constraints as follows:
- In Align Trailing to:, change Safe Area.Trailing to Superview.Trailing.
- In Align Bottom to:, change Safe Area.Bottom to Superview.Bottom.
As you can see, your constraints now override the Safe Area Guide and display their expected behavior.
Congrats, you’ve learned the basic techniques of Adaptive Layout!
Where to Go From Here?
Take a moment to think about the app you’ve built (or the completed project you’ve downloaded). In particular, consider that your app looks really good on all devices, in both orientations, using only one storyboard file!
If nothing else convinces you that Adaptive Layout is the way of the future, consider the fact that this layout will also look good on devices that haven’t even been released yet.
The takeaway from this tutorial is that, as a developer, you’ll need to change your approach to app design. Rather than working towards pixel-based layouts, you should consider the relationship between the UI elements on the screen.
If you want to learn more about Adaptive Layout, check out our Adaptive Layout video tutorial series, which takes you all the way from a beginner to an Adaptive Layout master! It’s also useful to watch Part 1, and Part 2 of Making Apps Adaptive from WWDC 2016.
In the meantime, if you have any questions or comments about this tutorial or Adaptive Layout in general, please join the forum discussion below!