The Ultimate Guide to iOS AutoLayout (II): Responsive AutoLayout

March 26, 2017 in

Hi there! This is the second episode of “The Ultimate Guide to iOS AutoLayout”. In the previous episode, we mastered the basics of AutoLayout, learned about conflicts, ambiguity, and how to solve them to craft awesome user interfaces. Today, we are going to dive a little bit deeper into responsive AutoLayout interfaces by switching from fixed dimensions to responsive AutoLayout positioning and sizing. Let’s start!

Our Sample Project

To illustrate the problem with absolute offsets and sizes, we will use a sample app that mimics a social network profile for a user. Maybe the designer in our team gives us some initial designs, depicting how the application looks in an iPhone 7Plus.

The Ultimate Guide To AutoLayout (II): Responsive AutoLayoutYou can see a picture of how the main screen of the application should look like. It’s a standard social network profile screen. We have a header image on top, followed by a rounded user avatar, the name and email of the user, and then two main sections: a “Favorite pics” collection view with the favorite pics of the user, and a “Favorite places” map showing the favorite cities for the user. Pretty straightforward.

For the purposes of this example, let’s suppose the designer is kind of amateurish (let’s suppose it’s me ;) and he gives you no further indications on how the elements should distribute in other different devices, like an iPad Pro or an iPhone 4S. Thus, you are left to decide how to exactly translate this design into a responsive user interface, one that adapts to all kind of iOS devices while still looking good.

Let’s have a look at how not to do it first, so we can learn how to do it right later.

The Problem with absolute positioning and sizing

The following video shows how we would build the user interface from the design using absolute positions and sizes:

First, we add the header image, pin it to the top, left and right edges and set its height to 140.

  • Then, we add the image for the user avatar. We pin it close to the left border of the screen, where our content margin lies, and we set a fixed height and width of 80 pixels. We align it to its bottom and set an offset of 40 (half the size of the image).
  • Next, we add labels for name and email. We’ll set the first one at the right, extend it’s length until it reaches the margin of the avatar, and align their top edges. We position the email label below the header, extending it to the right edge of the screen and the avatar image. Aligned left, this label will have a lighter, gray text color.
  • Then, we will add the “Favorite Pics” section. We add a label just below the avatar. We align this label correctly on both sides, and we add a collection view below. The collection view in the design shows two rows of square, 80px-side images. We add a template 80×80 cell, and 10px of span, totaling 190px.
  • Regarding the collection view cell, we add an image covering the whole cell. For this sample project, the image is included in the project’s assets and hardcoded.
  • Next, we add the “Favorite Places” section. This section includes (top to bottom) a label, a map and a button. We pin the label to all edges, the map to all edges too, and then the button to the map and the bottom of the view without defined size. The map, thus, will grow as the screen grows.

Our Initial Results

This configuration looks nice on the iPhone 7Plus, because we are building our interface precisely with that device’s dimensions. However, when we display it on an iPad Pro or iPhone SE, the results don’t look that good.

This is a preview of how our interface will display in the different devices.

As you might notice, the map gets extremely bigger or thin, to the point of disappearing in an iPhone 4S. Also, the header and avatar look nice in the iPhone 7/7Plus, but they are too big for an iPhone 4S/SE, and they are definitely too small for the big iPad Pro. In order to solve that, we need to change our way of thinking in constraints. We need to switch to a responsive AutoLayout mindset.

Switching to Responsive AutoLayout

Let’s take care of the two main problems of our absolute layout: the distribution of the elements (header, collection view, map) and the size and position of the avatar image.

Distribution of Elements

The first thing we need to change is how we position and size the three main elements of the screen, the ones that might grow or shrink. I’m referring to the header, the collection view and the map.

Instead of thinking on absolute positions and sizes, abstract the current values of your design and think how they translate to proportions. For example, the header image, even though it’s 140px on an iPhone 7Plus, takes approximately 1/6 of the screen’s height. Thus, we can set it to that proportional height as show in this video:

  • First, remove the constant height constraint
  • Next, add a equal height constraint with the parent view
  • Finally, change the 1:1 relationship to 1:6.

Next, we do the same process with the map, given that it takes approximately 1/5 of the screen’s height. Finally, as the pictures seem to be the most important part of the user’s profile, we remove the height constraint for the collection view, allowing it to grow on larger screens, and knowing that it will not shrink past certain point (as the map and header image take only 1/5 and 1/6 of the screen respectively).

Adjusting the header view and the map

Now it’s time to adjust the avatar image. It definitely needs to adapt as the screen size changes. How? Looking at the design, we can relate it’s size with the size of the header approximately 1/2 to 1/3. First, we remove the fixed height constraint, and set a 1:1 ratio on width, so the image will be a square. Then we remove the width constraint, add a “equal height” constraint to the header, and change it from 1:1 to 1:2. You can see in the video how I experiment with different ratios:

Now, one issue that appears is that the avatar image is not aligned to the center of the header’s bottom line anymore. This is because the dimensions of the image and the header itself have changed, and will change for every device. In this image, you can see how the avatar image is incorrectly positioned due to the offset of the bottom constraint. Let’s solve this.

Setting the avatar image’s position programmatically

Sometimes you can’t just use Interface Builder alone for crafting your user interface. In some user interfaces, you need to adjust some constraints programmatically.

Our avatar image’s center is set to a fixed distance from the bottom of the header image. Even though we could instead use a constraint from the center of the avatar to the bottom of the header. However, let’s use this situation as an example of modifying a constraint programmatically.

Thus, to correctly position it, we need to change the constraint’s constant that defines its center’s distance to the header’s bottom. In order to do that, you need to create an outlet from that constraint. First, click on the avatar image, then click on the constraint that links the center of the image with the header. Then, create an outlet like you have learned to do with labels, buttons, etc. The image below shows you how to do this:

Name the variable “avatarVerticalSpaceConstraint”. You’ll notice it’s type is “NSLayoutConstraint“. This is the type for AutoLayout constraints, and we’ll talk about them in depth in episode IV of this guide. Once we set our outlet, we are able to access that variable in code, so now we can adjust it. The question is, when exactly?

Adjusting Custom AutoLayout Constraints

Under AutoLayout, after a view controller finishes applying the different layout constraints to all of its views, it calls viewDidLayoutSubviews. This is the perfect moment for applying our own changes, as all the other elements will have been correctly positioned, and adjusted to the size of the device. Thus, we will set this distance constraint to half of the size of the avatar image, effectively positioning it at the center of the header image’s bottom line.

override func viewDidLayoutSubviews() {
   // set the vertical space constraint
   avatarVerticalSpaceConstraint.constant = avatarImageView.frame.size.width / 2.0

That constant property is the same one that you can modify in Interface Builder when editing a constraint. Now, if you run the app in the simulator, you’ll notice how the interface is more coherent in all device types.

Polishing Touches

Our user interface is definitely looking better now. However, there are yet some small details we can add to make it look extra-awesome. Let’s describe some of them:

Tweaking the Collection View

In the article Towards Responsive iOS Design, I describe a mechanism I call “Flowing Responsiveness” for iOS collection views that will allow them to adapt the size of their cells according to the size of the device. We can use a variation of this technique here to make our pics section more responsive by setting an adaptive size for its items. We will set a fixed amount of three rows of pics for small devices, and five rows for bigger devices. Then, we will adapt the size of the cells accordingly.

In order to do that, we need

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: IndexPath) -> CGSize {
   let numberOfRows = traitCollection.horizontalSizeClass == .compact ? 3.0 : 5.0
   let cellSpan = 10.0
   let sideSize: Double = floor((Double(collectionView.frame.size.height) - ((numberOfRows-1.0)*cellSpan)) / numberOfRows) - cellSpan
   return CGSize(width: sideSize, height: sideSize)
// reacting to transitions.
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {

Every collection view has a collection view layout that determines how the items are arranged, it’s position and size, and how they flow in the collection view. The delegate for the collection view becomes also the delegate for its layout.

We can then use the collectionView:collectionViewLayout:sizeForItemAtIndexPath: to define the size of our cells in runtime. This will allow us to use this clever trick of dividing the total height of the collection view’s current frame between the number of rows (plus the spans in between) to come up with the perfect size for the items in the collection view.

We will also control transitions in size (i.e: rotations) via the viewWillTransition: method. This is for completion purposes, and not strictly required for our portrait demo project.

Rounding the Avatar Image

Usually, for rounding a square UIImageView, we need to set its cornerRadius to half the side of the image. Again, in AutoLayout, this needs to be done when the size of the image has been finally calculated and adjusted. Thus, the perfect method for doing that is the viewDidLayoutSubviews that we used earlier to set the constraint programmatically.

override func viewDidLayoutSubviews() {
   // round the avatar image
   avatarImageView.layer.cornerRadius = avatarImageView.frame.size.width / 2.0
   // set the vertical space constraint
   avatarVerticalSpaceConstraint.constant = avatarImageView.frame.size.width / 2.0

Finally, the image will be a circular image, correctly positioned at the bottom of the header image.

Final Result

This is the final result for our user interface. Compare that with the initial image at the beginning of this article.

Pretty cool, right? We have improved a lot how our screen is displayed from the tiniest iPhone to the biggest iPad.

Where to Go Next

In this episode of “The Ultimate Guide to iOS AutoLayout“, we learned why approaching a design with a responsive mindset is essential for building great user interfaces. You might find yourself in this scenario when the designer of your team sends you a screen flow that only includes one kind of devices. In this situation, if you can, you should always ask the developer about the responsiveness of the elements in the screens. This will help you decide how to assign proportional dimensions and relative positions to the different elements.

You might have noticed that there are things that might still be improved to make the design look gorgeous on every device, concretely, the font sizes don’t seem to be right for the smallest or the biggest devices. In the next episode of the series, we are going to discuss what size classes are, and  how to use them to tweak some visual elements depending on the size of the device.

Finally, you can download the source code for the sample project in my Github repository, as always. Hope you’ll find it useful.