Beginning Auto Layout in iOS 6: Part 2/2

Update note: Check out our newer version of this tutorial, updated to Swift and iOS 8: Beginning Auto Layout Tutorial in Swift: Part 2. This tutorial is an abbreviated version of one of the chapters from our new book iOS 6 By Tutorials. Matthijs Hollemans wrote this – the same guy who wrote the iOS […] By Matthijs Hollemans.

Leave a rating/review
Save for later
Share
You are currently viewing page 4 of 5 of this article. Click here to view the first page.

Painting the portraits

Drag a label onto the green view. Notice that now the guides appear within that green view, because it will be the superview for the label.

Drag label into green view

Position the label in the top-left corner against the guides. This will add two Space constraints to anchor the label in the top-left corner of the green view:

Label constraints

Notice that these two new Horizontal and Vertical Space constraints are listed under the green view’s Constraints section, not in the main view.

Now move the green view around a bit. You’ll see that only the constraints between the green view and its superview change, but those for the label don’t. The label always stays put in the same place, relative to the green view.

Select the label and place it against the bottom margin, horizontally centered. Then drag a new image view object on to the nib, and make the layout look like this:

Image view in gallery

The image view is pinned to the top, left and right edges of its superview, but its bottom is connected to the top of the label with a standard spacing.

Download the resources for this tutorial and unzip the file. You will find an Images folder – add this folder into your project. Set Ray.png as the image for the image view, change the image view’s mode to Aspect Fit and set its background color to white. Change the label’s text to say “Ray”.

Your layout should now look like this:

Gallery with Ray

Notice that Interface Builder has placed a Height constraint on the label now. This happened the moment you set the image on the image view.

Label fixed height

Interface Builder tries to prevent what are known as ambiguous layouts. If neither the image view nor the label has a fixed height, then Auto Layout doesn’t know by how much to scale each if the height of the green view should change. (Interface Builder seems to ignore for now that the green view actually has a fixed Height constraint set on it.)

Let’s say at some point in your app the green view becomes 100 points taller. How should Auto Layout distribute these new 100 points among the label and the image view? Does the image view become 100 points taller while the label stays the same size? Or does the label become taller while the image view stays the same? Do they both get 50 points extra, or is it split 25/75, 40/60, or in some other possible combination?

Auto Layout is not going to guess, so Interface Builder “fixes” this problem for us by giving the label a fixed height. It could also have given the image view a fixed height, but the label makes more sense.

For now, let’s just live with the Height constraint on the label.

Note: The proper solution to this small layout problem is to change the “Content Compression Resistance Priority” of the label. You will learn more about that later on. If you can’t wait, then go into the Size inspector for the label and set the vertical Content Compression Resistance Priority to 751. The Height constraint on the label should now disappear.

Note: The proper solution to this small layout problem is to change the “Content Compression Resistance Priority” of the label. You will learn more about that later on. If you can’t wait, then go into the Size inspector for the label and set the vertical Content Compression Resistance Priority to 751. The Height constraint on the label should now disappear.

Adding the other heads

Move the green view onto the main view’s top-left corner. Recall that the green view had Horizontal Space and Vertical Space constraints that determined its position in the parent view. It still has those, but they are now set to a value of 0 – they are represented by the thick blue lines (with white borders) at the top and left edges of the window:

View in top-left corner

So even though the view sits completely in the corner, it still needs constraints to anchor it there. Think of these as margins with a value of 0.

Select the green view and tap Cmd-D to duplicate it. Move the duplicate into the top-right corner:

Green view in top-right corner

Duplicate two more times and put these copies in the bottom-left and bottom-right corners, respectively.

Change the screen design to the following:

Gallery design

Those are some good-looking programmers! :-)

Run the app. It looks good in portrait, but not so much in landscape:

Gallery looks bad in landscape

It should be pretty obvious what went wrong: you’ve set a fixed width and height on the four brightly-colored container views, so they will always have those sizes, regardless of the size of their superview.

Select the Width (160) and Height (230) constraints from all four views and delete them. If you run the app now, you’ll get something like this. Also not very good:

Still looks bad in landscape

This looks very much like the problem we solved in the introduction, so if you think back to how we solved that, you’ll recall that we gave the views equal widths and heights.

Select all four colored views and choose Pin\Widths Equally. Select the views again and choose Pin\Heights Equally.

Run the app again and rotate the device. Hmm… it still looks exactly the same as before. Why?

Well, if you look at the screenshot you’ll see that all the views do have the same height, and they also appear to have the same width (the green and brown views are partially obscured by the yellow and blue ones), so our constraints are being met. It’s just not the width and height that you want them to have. There must be other constraints that are getting in the way.

Sure enough, if you look at the constraints on these views, you’ll see that they also have Horizontal and Vertical Space constraints that force them into place (look at list of constraints on the main view, not the four subviews):

Bad H-space

What’s worse, you can’t even delete that constraint. Its T-bar is not bold and the constraint is not blue, so Interface Builder put it there in order to prevent a layout problem.

So why does it do that? Just saying that all four views must have equal sizes is not enough to determine what those sizes should actually be, because Auto Layout does not know how these four views are connected to each other. They appear side-by-side in the design, but there are no actual constraints between them. Auto Layout does not know that it needs to split the window width between the “Ray” and “Matthijs” boxes.

If Auto Layout can’t figure this out by itself, you have to tell it.

To be related

Select the Ray and Matthijs boxes and choose Pin\Horizontal Spacing. Because the boxes are side-by-side, this adds a Horizontal Space constraint with size 0 between them, and that is enough to let Auto Layout know how these two views are related.

Important: Interface Builder does not automatically remove the leading Horizontal Space between the superview and the yellow box (the one from the screenshot above), but it did promote it to a user constraint (a fat bar). You can now delete this space. If you don’t, you will get an “Unable to simultaneously satisfy constraints” error during runtime when you flip to landscape.

Run the app. It should now look like this:

Landscape a bit better

That looks a bit better already. The four boxes now have equal widths, but the heights are still wrong. The solution is similar: put a Vertical Space between the Ray and Dennis Ritchie boxes and remove the Vertical Space between the Dennis Ritchie box and the top of the window.

Run the app again, and this time it looks all right:

Gallery landscape OK

Notice that the “Dennis Ritchie” label is not centered below its image view. This originally happened to me when I typed that text into the label. The label was initially centered in the view, but Interface Builder decided it knew better and replaced that centering constraint with a Horizontal Space. If this happened to you, too, then select that label and choose Align\Horizontal Center in Container to fix it.

A quick note on the image views: they stretch out because you have not given them a fixed size. You may not know it, but that’s intentional on your part. ☺ The image views wouldn’t fit in landscape mode otherwise. However, if you want an image view to keep its original aspect ratio, then you’re out of luck. You cannot achieve the following effect using Interface Builder:

Aspect ratio on images

Unfortunately, Interface Builder does not currently provide a way to make constraints that keep the aspect ratio of a view intact. To do that, you need to create and set the constraints programmatically. You will learn how to do that in “Intermediate Auto Layout” in iOS 6 by Tutorials.

Tip: You have seen that you can preview what the UI will look like in landscape by changing the Orientation setting under Simulated Metrics. You can also test the resizing behavior of your views directly in Interface Builder.

Select the main view. Under Simulated Metrics, set Size to Freeform. This adds resize handles around your nib that you can use to mold it into any shape you want. Auto Layout will recalculate the layout on-the-fly:

Freeform

However, be careful with this. Sometimes Interface Builder will insert new constraints of its own when you’re resizing, as it did here in the bottom-right corner (it added a Horizontal Space). It may also delete existing constraints when they fall outside of the nib bounds.

Contributors

Over 300 content creators. Join our team.