At this year’s WWDC, Apple introduced Adaptive UI, a new method of building apps that are adaptive to the size and characteristics of the container they are run in instead of targeting a particular device. This is a move that Apple have been pushing since the introduction of Auto Layout in iOS 6. Then, you could ignore Auto Layout and still build an app that worked as expected. With the introduction of new devices to the market and changes to Xcode and the iOS SDK, developers will have to grasp the components of building adaptive layouts which includes size classes, trait collections, adaptive view controllers etc.
In this article we will cover the creation of adaptive user interfaces using Interface Builder. We will look at the use of Size Classes to lay out user interface components that will render differently depending on the Size Class. Not only can you use Size Classes to create adaptive layouts, we will also look at how you can have fonts, images and views appear differently according size classes.
Getting Started with Size Classes
A core concept of Adaptive UI is the use of Size Classes. Size classes define the canvas size used in layouts. When creating an app’s UI, you specify layout rules based on Size Classes and these will determine how your UI looks and changes when the available size of your view controller changes.
A size class can be either compact or regular and it identifies a relative amount of display space for the height (vertical) and for the width (horizontal). For example an iPad will have regular width and height in both portrait and landscape orientations while an iPhone in portrait will have a compact width and regular height. The following table shows Apple devices and their corresponding size classes.
Vertical Size Class | Horizontal Size Class | |
iPad Portrait | Regular | Regular |
iPad Landscape | Regular | Regular |
iPhone | Regular | Compact |
iPhone Landscape | Compact | Compact |
iPhone 6 Plus Landscape | Compact | Regular |
You should note that a size class doesn’t necessarily map to one device in one orientation. For instance, an iPad can have a view with an iPhone style layout (i.e. compact horizontal and regular vertical size class) when presented in a smaller space on the device and an iPad style layout (i.e. regular horizontal and regular vertical size class) when the space is larger.
This makes it possible to have a unified storyboard when building a universal application, previously, you had to design two separate storyboards – for iPad and iPhone.
You can find the final project we will be working through on GitHub.
To get started, first create a Simple View Application. Name it AdaptiveLayout
and make it universal. Chose whichever language you prefer.
When you open Main.storyboard
you will see an almost square canvas that does not correspond with any device. At the bottom of the canvas is the Size Class control that enables you to change size classes. Interface Builder starts in the “any width and any height” size class, where you can lay out common UI components and constraints for the different screen sizes and orientations. You then update the components that vary when the available screen size changes by amending the UI in the different size classes.
Drag a View
and an Image View
from the Object Library onto the view controller canvas. Place the view at the top of the main View and adjust its size so that it occupies about half of the view. Place the Image view below this view and have it occupy the remaining half. You don’t have to be accurate here, we’ll be using Auto Layout to lay out the view. You can download some image assets here to use in the project. You can use your own images, but the download contains images that have already been sized for iPhone (1x, 2x, 3x) and iPad (1x, 2x).
We’ll add the images to the Asset Catalog. In the Project Navigator, open Images.xcassets
, click on the + sign at the bottom of the left panel and select New Image Set from the popup menu. Double click on the image set’s name and rename it motobecane
. Create another image set and name it focus
. You will notice a new display scale that wasn’t there in previous iOS versions – 3x. This new display scale is for Retina HD and is found on the iPhone 6 Plus. There are 9 times more pixels in a 3x image than a 1x image i.e. in each direction, you have 3 pixels making up one point.
From the file you downloaded locate motobecane_small.png and drag it to the 1x box in the motobecane
image set. Drag the motobecane_small@2x.png to the 2x box and motobecane_small@3x.png to the 3x box. Do the same for the focus_small.png images, dragging them to their respective 1x, 2x and 3x boxes.
In Main.storyboard
select the Image view and in the Attributes Inspector, set its Image to motobecane
and on the View pane, set the Mode to Aspect Fit.
Select the View, then in the Identity Inspector, locate the Document Pane, set the Label to DescriptionView
. This is so we can identify the view in the Document Outline. From now onwards I’ll refer to it as DescriptionView
to distinguish it from the main View. Set the background color of this view to ‘Dark Gray Color’. Do this by choosing from the preset colors in the Background dropdown menu in the Attributes Inspector.
Drag two labels to the DescriptionView
and set their Color to white and Font to Helvetica Neue Thin. Set the font size of one to 50 and the other to 25. Place the label with the bigger font at the top of DescriptionView
and the other below that. Set the text of the top label to “2010 Motobecane Grand Sprint” and for the other label, set the text to “Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.”. For this label, set its Lines to 0 in the Attributes Inspector. Then adjust its size on the canvas so that all the text shows.
You should have something roughly as shown below.
Instead of running the app on different devices and in different orientations to see what the view looks like, you can use the Preview Assistant Editor. Click on the Assistant Editor in the menu bar.
The screen will split into two panes. On the pane with the code, click Automatic, chose Preview from the drop down menu, and select Main.storyboard.
You will see a preview of the storyboard on an iPhone 4-inch. On the bottom left side of the pane click the + icon and select iPhone 4.7-inch to add it to the pane. Also add the iPhone 5.5-inch and the iPad. For the iPhone 4.7-inch, hover on its label at and click on the icon that appears. This will rotate the device to landscape orientation. As you can see, the devices show you a preview of your layout as it is rendered on each device. When you make changes to the storyboard, the changes are immediately reflected in the Preview Assistant Editor. This can save loads of time when working on the UI. Without setting constraints, the UI looks off on just about every device.
With that set up, we’ll start adding constraints to the views.
Select DescriptionView
and select the Pin menu found below the canvas. Set the left, top and right spacing to 0 and make sure Constrain to margin is unchecked (this prevents padding from getting added around the view). Click the Add 3 constraints button.
Select the Image view and set the left, bottom and right spacing to 0. Again, make sure Constrain to margin is unchecked (For the rest of the tutorial, make sure it is unchecked. I will assume this and won’t mention it again).
In the Document Outline, Control-drag from the DescriptionView
to The main View.
Select Equal Heights from the menu that appears. Select this constraint from the list of added constraints.
In the Attributes Inspector, set the Multiplier to 0.4. With this, the DescriptionView
will take up 40% of the main View. Make sure the First Item is DescriptionView.Height. If it isn’t, select Reverse First and Second Item from the dropdown menu.
In the Document Outline pane Control-drag from the Image View to DescriptionView
and select Vertical Spacing. Select this constraint and in the Attributes Inspector set the Constant to 0. This makes the Image view start where the DescriptionView ends with no space between them.
If you are having trouble identifying the constraint you just added, select the view you added the constraint to and in the Size Inspector you can see all constraints added. You can edit a constraint from here or you can double click on the constraint and go to the same panel we have been using to edit them, as shown above. But the constraints naming should be enough for you to identify an added constraint. (For top and bottom spacing, you might see it related to Top/Bottom Layout Guide instead of Superview. If you want the constraint related to superview, when you are adding the constraint, make sure that View is selected on the Top/Bottom constraint dropdown in the Pin menu).
Select the top label (with the “2010 Motobecane…” text) and add a constraint with a Leading space of 20, Top space of 40 and Trailing space of 20.
Select the bottom label and add a constraint with Leading, Trailing and Top spaces to 20.
To make the canvas correspond with the constraints set up, select View
from the Document Outline, then Editor -> Resolve Auto Layout Issues and under All Views, select Update Frames.
When the iPhone is in portrait, it would be best to present two panes for the views because the 40% screen size for DescriptionView
is too small.
Change the size class of the canvas to any width, compact height from the Editor menu. Notice the bottom bar, with the Size Class control turns blue. This shows that you are now working on a specific Size Class and any constraints you set will only affect that size class.
Select the Image View. In the Size Inspector select the Top Space to DescriptionView constraints and press Delete.
This will not delete the constraint. Rather uninstall
it from that Size Class. If you want to uninstall a constraint by selecting it from the Document Outline, then uninstall it with Command-Delete. If you only press Delete, you will delete the constraint and this will be a change for all Size classes.
Uninstall the Trailing Space to SuperView constraints by selecting it and pressing Delete. You can see the uninstalled constraints by selecting the All control at the top of the Constraints panel. They appear grayed out in both the Size Inspector and Document outline.
If you double-click on an uninstalled constraint to see its attributes in the Attributes Inspector, you will see an additional line at the bottom which indicates that the constraint is installed for the base layout but not for the any width, compact height layout.
With the Image View still selected, add a Top space to SuperView of 0. Make sure that it is related to Superview and not Top Layout Guide (or DescriptionView) by selecting View from the constraint’s dropdown menu.
In the Document Outline, Control-drag from the ImageView
to DescriptionView
and select Horizontal Spacing. Select that constraint and give it a constant of 0. Make sure First Item is DescriptionView.Leading and Second Item is motobecane.Trailing. Use the dropdown menus to set this. Then, Control-drag from the Image View to the main View and select equal widths. Select the constraint and give it a Multiplier of 0.5. Make sure First Item is motorbecane.Width and Second Item is Superview.Width. At this point, you will see several red lines on the canvas. This indicates that you have conflicting constraints. We’ll fix these shortly.
Select DescriptionView
. Uninstall its Leading Space to SuperView constraint. Uninstall its Proportional Height SuperView constraint. From the Pin menu, set its Bottom Space to SuperView to 0. If instead XCode adds a Bottom Space to Bottom Layout Guide, you can change this from the dropdown menu of the constraint, selecting View from the options.
You should have the following constraints installed for the Image View and DescriptionView.
Update the canvas by selecting View from the Document Outline, then Editor -> Resolve Auto Layout Issues and under All Views, select Update Frames.
Now the iPhone landscape view looks better.
Size Class Dependent Fonts
The current font size looks good in the iPad view but is too large for compact size classes. We’ll look at how to override font sizes within your size classes.
To do this, we won’t change the font configuration for a particular size class. If you change the font configuration for a size class, the change will affect the base layout. Instead, do the following.
Change the size class to the base Any Width, Any Height. Select the “2010 Motobecane Grand Sprint” label and open the Attributes Inspector. Click the + to the left of Font.
A menu will pop up that lets you select the size class combination to override the font size. Select Compact Width > Any Height. A second font selector will be created with a size class label next to it (in my case – wC hAny). Change the font size in this new selector box to 30.
Repeat the above for the “Lorem Ipsum” label, setting the font size to 15 in the added selector box.
Looking at the preview, our layout looks better, but the first label is clipped.
We could keep on tweaking the font size until it fits on all the devices, but this isn’t a scalable option. If this were a real app, then the text in that label would likely be dynamic and so making the font size perfect for a particular string literal will not work for another string literal, especially for a longer string.
To make sure the text always fits the label, select the label and open the Attributes Inspector. Change AutoShrink to Minimum font scale and make sure that it is set to 0.5.
The text will now scale to fit the label.
Size Class Dependent Images
You might want to provide different images for different size classes. To do this, open Images.xcassets
and select the motobecane set. Open the Attributes Inspector and change Devices to Device Specific and make sure iPhone and iPad are selected. This adds the iPad group to the list of device specific files.
From the folder you downloaded earlier, drag the motobecane_big.png to the iPad 1x box and the motobecane@2x.png to the 2x box. You should have something as shown below. (The iPad images provided are larger than their iPhone counterparts, and for this tutorial, I’ve added text to distinguish them from the iPhone images).
Looking at the preview, you can see that the iPad has a different image on it (The image has an “iPad” label on it).
Size Class Dependent Views
When creating universal applications, providing different layouts with the techniques we have looked at so far might be enough to make the UI look good on all devices. At times, you might find that the UI of larger devices looks a bit vacant and wanting. Sometimes, larger views require more content to look good.
It is possible to include a view in your storyboard file that only renders for particular size classes. You can set up your UI so that on larger devices, more content will be available to the user, while the same app on a smaller device will only show the basic, most relevant data.
To get started, first add an ImageView
(You can do this with any View object) to the DescriptionView
. From the Pin menu, set its Trailing and Bottom space to 0 and give it a height of 132 and width of 150.
In the Attributes Inspector, set Image to focus
. Set the Mode to Aspect Fit.
The view appears in every device and orientation but we only want it on iPads.
We need to uninstall the view in the size classes corresponding to the iPhone devices. Select the Any Width, Compact Height size class. In the Document Outline select the Image View (it is now labelled focus
, if you used the downloaded image) and hit Command-Delete. This will uninstall the view from that size class (Be careful not to hit Delete alone, you will delete the view from all size classes).
Change the size class to Compact Width, Regular Height and uninstall the view for this size class as well. Now if you look at the preview, you will only see the image on the iPad.
Conclusion
Adaptive layouts are a fundamental part of iOS 8. The future of app design is adaptive and developers will need to grasp this concept if they plan on building apps that work well on Apple devices.
We have looked at how to use Interface Builder to build adaptive layouts. This is a good start to making adaptive apps but there is a lot more to be said on this topic. In the future we will have an article covering UIKit to create adaptive apps. We’ll look at adaptive view controller hierarchies, using trait collection and the UISplitViewController
which was previously only supported on the iPad but now can also be used on iPhones.
I am a web developer who dabbles in mobile development from time to time. You can find me on Twitter @joyceechessa to see what I’m up to.