Using Advanced Auto Layout Techniques to Adapt Interfaces to Screen and Content

Development iOS

At Savvy Apps, we've been using Auto Layout since 2012, back when it was first introduced in iOS. Auto Layout is a powerful tool that makes creating dynamic interfaces much easier, requiring very little or no coding at all. We've noticed that in many cases Auto Layout is not used to its full potential. This improper usage sometimes leads to layouts that are too constrained and don't adapt well to changes in content or font size.

Over time we've discovered more advanced techniques to take full advantage of Auto Layout capabilities. For example, by combining Auto Layout, stack views, and intrinsic content size in particular, we've kept our view controllers free of layout code and created layouts that effortlessly adapt to different content and font sizes. In this resource we demonstrate how to take advantage of these techniques to make developing dynamic user interfaces easier. We also include a sample project so you can get hands-on with the techniques we talk about in this resource.

Understanding Auto Layout, Stack Views, and Intrinsic Content Size

Before we dive in, let's take a moment to understand the three main components that work together to improve dynamic user interfaces: Auto Layout, stack views, and intrinsic content size.

Auto Layout is an engine built into iOS that takes care of calculating the final position and size of the views in a user interface. If you'd like to learn more, check out Apple's WWDC video on Auto Layout and Ray Wenderlich's Auto Layout tutorial.

Stack views, or UIStackView, are a kind of UIView subclass that can automatically manage the position and size of the views it contains. We recommend reading Ray Wenderlich's stack view intro to familiarize yourself with this tool.

Intrinsic content size (intrinsicContentSize) is the property of a UIView that represents the minimum required size of the view in order to display all of its content. In the case of a UILabel, for example, it would be the minimum required size to show all the text contained in the label without being cropped. Some system views, like buttons and labels, automatically calculate their intrinsic content size based on their content. Custom UIView subclasses require the developer to implement their intrinsic content size. Check out Apple's documentation on using intrinsic content size in a view to learn more.

Now that we know the basics of these three elements, let's look at how they all work together.

Using Intrinsic Content Size to Adapt an Interface to the Content

One of the ways we create interfaces that adapt to the content size is by leveraging Auto Layout with intrinsic content size. If an app has dynamic content, like content that is fetched from the cloud, it usually can vary in length or dimensions. In that case, it's essential that the app handles content of different dimensions.

The Auto Layout engine uses a set of constraints to calculate the position and size of all the views in the screen. When a constraint is not set for a given dimension, say if a label doesn't have a “height” constraint, it can use that view's intrinsic content size (if available) to calculate that dimension. By taking advantage of this, we can make interfaces that utilize this intrinsic content size to adapt to changes in content.

In this example, the selected label has four constraints. The top, leading, and trailing constraints are going to define its Y position, X position, and width, respectively. Since the height is not constrained, the height will be calculated based on its content. The final (bottom) constraint will push the image down as long as the last view in the constraint chain (the button, in this case) doesn't have a constraint at the bottom that fixes it to a certain distance from the bottom (or if that bottom constraint is fixed to a scroll view that will be allowed to expand its content size).

As we mentioned earlier, some UIView subclasses already manage their own intrinsic content size, but there are many that don't. If you're using a custom subclass, you'll have to do it on your own or implement it into existing system subclasses.

For example, if we wanted to embed a UITableView into a view hierarchy, the layout might already be inside a scroll view, so we don't need our table view to scroll. Instead, we need it to be as big as its contents require.

To do this we can subclass UITableView to return its contentSize as the intrinsicContentSize. This will cause the table view to dynamically adjust its height to be large enough to display all the cells without requiring to scroll.

The other thing we need to do to make sure this works is to call invalidateIntrinsicContentSize() whenever we think it might have changed. In this case, we can do that whenever the table view's contentSize is changed.

Allowing interfaces to adapt to content doesn't just make the project much easier to maintain. In cases when the content is dynamic, it's a requirement. Using Auto Layout instead of calculating the content size in code makes the interface a lot easier to develop and maintain. Managing the layout with Auto Layout rather than code in our View Controllers can also help us keep our files small and easier to manage.

Adding Stack Views to Make Interfaces Even More Dynamic and Modifiable

Stack views are container views with the unique property that they manage the position and dimension of their subviews automatically. They do this by creating all the necessary constraints for their views and using the contained views' intrinsic content size when available. Using stack views with Auto Layout makes it very easy to add, remove, and reorganize the contained views.

To make your interface easier to modify:

  • Use stack views to easily add/remove elements of your UI.
  • House your stack views in scroll views.
  • Nest your stack views.

In this example, a very similar layout (minus the button) is achieved here by using a stack view and five constraints. If you tried to build this type of layout without using a stack view, you would need to use two to four constraints for every view inside the stack view. That's around 24 in total.

We've used this technique in apps where the registration and login screens have a similar layout. To create these easily we started with a template that included the background image, a scroll view (in case the content grew bigger than the screen), a white container view, and a stack view with a customer's logo.

Creating all the screens from that template was as easy as dragging the different elements, like labels, buttons, and text fields, to the stack view. The stack view took care of adjusting the position and size of each one of them, as well as the size of the container. In the cases where the content was so much that it outgrew the screen, usually in the smaller screens like the iPhone SE, the scroll view took care of making the whole content scrollable.

Stack views also allow us to easily make our interfaces dynamic by hiding views contained in them. In many situations you'll want your app to hide or display certain elements based on different conditions, like a switch that toggles a view with advanced options. Check out the animated example on the right for a glimpse at how something like this might look in your app. In this example, the user taps a button and the view changes to display additional information. This can be tricky to do by managing constraints, but it becomes trivial once you use a stack view.

Adjusting the UI to show or hide any number of views inside a UIStackView is as easy as setting the isHidden property of those views within an animation block. The stack view will handle the position of the remaining views.

As a word of caution, make sure you check the value of isHidden before setting it. If you set it to its current value, your view might not hide or unhide properly. A simple example would look like this:

let newHiddenValue = viewShouldBeHidden()
UIView.animate(withDuration: 0.25) {
    if view.isHidden != newHiddenValue {
        view.isHidden = newHiddenValue
    }
}

Combining All Three: Auto Layout, UIStackView, and intrinsicContentSize

Since Auto Layout, stack views, and intrinsic content size are all elements of the layout engine in iOS, it just makes sense to use them together to take full advantage of the layout engine. By combining all three, you'll write less UI and animation code. This will help you keep your view controllers slim and focused on the logic of your app, as well as help you avoid the “Massive View Controller” pattern.

By utilizing stack views in particular, you'll find that it's very easy to reorganize the content or add more labels without the need to adjust any constraints or any of the existing views. By allowing your views to use their intrinsic content size to determine their dimensions, you'll save time trying to calculate those dimensions in your code.

We put together a basic, hands-on example so you can see how everything we talked about works together. Click here to download the sample Xcode project on Dropbox.

Note that there are a couple of bugs in Interface Builder in some versions of Xcode, mostly related with UILabels with numberOfLines set to 0, that can make working with it somewhat annoying. They are not hard to work around, and they seem to have been fixed in the latest Xcode version. These bugs won't show up on your app when it's running.

Concluding Note

Using Auto Layout with stack views and intrinsic content size allows you to create more powerful dynamic interfaces that are easy to manage and maintain. We'd love to hear your feedback on how you've used all three elements in your own apps. Be sure to keep in touch on Twitter and Facebook for updates, including announcements when we release additional educational resources and open source projects.

Emilio is an iOS developer with some experience in game development and design. He enjoys bringing concepts from game development into applications to improve user interactivity.

You made it this far so...