Start thinking in auto layout constraints!
Update note: This tutorial was updated to Swift and iOS8 by Brad Johnson. Original post by tutorial team member Matthijs Hollemans.
Have you ever been frustrated trying to make your apps look good in both portrait and landscape orientation? Is making screen layouts that support both the iPhone and iPad driving you to the brink of madness? Despair no longer, I bring you good news!
It’s not hard to design a user interface for a screen that is always guaranteed to be the same size, but if the screen’s frame can change, the positions and sizes of your UI elements also have to adapt to fit into these new dimensions.
The evolution of Auto Layout is quite the story. If you tried Auto Layout when it debuted with Xcode 4 and gave up, then you really should give Xcode 6 another chance. Xcode 5 and iOS 7 made it even better, and now with even more improvements in Xcode 6 and iOS 8 and the introduction of two new iPhone screen sizes, Auto Layout is a necessity in every iOS developers toolbox.
Not only does Auto Layout make it easy to support different screen sizes in your apps, as a bonus it also makes internationalization almost trivial. You no longer have to make new nibs or storyboards for every language that you wish to support, and this includes right-to-left languages such as Hebrew or Arabic.
So grab a snack and your favorite caffeinated beverage, and get ready to become an Auto Layout master!
The problem with springs and struts
Open Xcode 6 and create a new iPhone Swift project based on the Single View Application template. Call the app “StrutsProblem”:
You are no doubt familiar with autosizing masks – also known as the “springs and struts” model. The autosizing mask determines what happens to a view when its superview changes size. Does it have flexible or fixed margins (the struts), and what happens to its width and height (the springs)?
For example, with a flexible width the view will become proportionally wider if the superview also becomes wider. And with a fixed right margin, the view’s right edge will always stick to the superview’s right edge.
The autosizing system works well for simple cases, but it quickly breaks down when your layouts become more intricate. Let’s look at an example where springs and struts simply don’t cut it.
Click on Main.storyboard to open it in Interface Builder. Before you do anything else, first disable Auto Layout and Size Classes for this storyboard. You do that in the File inspector, the first of the six tabs:
Uncheck the Use Auto Layout box. This will tell you that size classes can’t be used either – that’s fine. Now the storyboard uses the old struts-and-springs model.
Note: Any new nib or storyboard files that you create with Xcode 4.5 or better will have Auto Layout activated by default. Because Auto Layout is an iOS 6-and-up feature only, if you want to use the latest Xcode to make apps that are compatible with iOS 5, you need to disable Auto Layout on any new nibs or storyboard files by unchecking the “Use Auto Layout” checkbox. Size Classes are compatible with iOS 7 and later.
Drag three new views onto the main view and line them up like this:
For clarity, give each view its own color so that you can see which is which.
Each view is inset 20 points from the window’s borders; the padding between the views is also 20 points. The bottom view is 280 points wide and the two views on top are both 130 points wide. All views are 254 points high.
If you’d rather not do this by drag and drop, you can open the size inspector and enter the following values:
- Top left: Origin 20,20, Size 130 x 254
- Top right: Origin 170,20, Size 130 x 254
- Bottom: Origin 20,294, Size 280 x 254
You can see what this looks like without having to run the app by using the new Preview Assistant. Open the Assistant editor, then from the top bar of the assistant editor (which will normally say “Automatic”) Choose Preview/Main Storyboard:
You can add as many simulated devices as you want to the preview assistant, which beats building and running on every different simulator! Click the + button on the bottom left to add a device. Set up two iPhone 4-inch screens, and rotate one of them to landscape using the button that appears next to the device name.
In landscape, the app doesn’t look quite right:
Instead, you want the app to look like this in landscape:
Obviously, the autosizing masks for all three views leave a little something to be desired. Change the autosizing settings for the top-left view to:
This makes the view stick to the top and left edges (but not the bottom and right edges), and resizes it both horizontally and vertically when the superview changes its size.
Similarly, change the autosizing settings for the top-right view:
And for the bottom view:
You’ll have seen the layout changing in the preview assistant. It will now look like this:
Close, but not quite. The padding between the views is not correct. Another way of looking at it is that the sizes of the views are not 100% right. The problem is that the autosizing masks tell the views to resize when the superview resizes, but there is no way to tell them by how much they should resize.
You can play with the autosizing masks – for example, change the flexible width and height settings (the “springs”) – but you won’t get it to look exactly right with a 20-point gap between the three views.
To solve this layout problem with the springs and struts method, unfortunately you will have to write some code.
UIKit sends several messages to your view controllers before, during and after rotating the user interface. You can intercept these messages to make changes to the layout of your UI. Typically you would override viewWillLayoutSubviews to change the frames of any views that need to be rearranged.
Before you can do that, you first have to make outlet properties to refer to the views to be arranged.
Switch to the Assistant Editor mode (middle button on the Editor toolset on the Xcode toolbar) and Ctrl-drag from each of the three views onto ViewController.swift:
Connect the views to these three properties, respectively:
@IBOutlet weak var topLeftView: UIView!
@IBOutlet weak var topRightView: UIView!
@IBOutlet weak var bottomView: UIView! |
Add the following code to ViewController.swift:
override func viewWillLayoutSubviews() {
if UIInterfaceOrientationIsLandscape(self.interfaceOrientation) {
var rect = self.topLeftView.frame
rect.size.width = 254
rect.size.height = 130
self.topLeftView.frame = rect
rect = self.topRightView.frame
rect.origin.x = 294
rect.size.width = 254
rect.size.height = 130
self.topRightView.frame = rect
rect = self.bottomView.frame
rect.origin.y = 170
rect.size.width = 528
rect.size.height = 130
self.bottomView.frame = rect
}
else {
var rect = self.topLeftView.frame
rect.size.width = 130
rect.size.height = 254
self.topLeftView.frame = rect
rect = self.topRightView.frame
rect.origin.x = 170
rect.size.width = 130
rect.size.height = 254
self.topRightView.frame = rect
rect = self.bottomView.frame
rect.origin.y = 295
rect.size.width = 280
rect.size.height = 254
self.bottomView.frame = rect
}
} |
This callback occurs when the view controller is rotating to a new orientation. It looks at the orientation the view controller has rotated to and resizes the views appropriately – in this case with hardcoded offsets based on the known screen dimensions of the iPhone. This callback occurs within an animation block, so the changes in size will animate.
Don’t run the app just yet. First you have to restore the autosizing masks of all three views to the following, or the autosizing mechanism will clash with the positions and sizes you set on the views in viewWillLayoutSubviews:
That should do it. Run the app on the iPhone 5 simulator (the preview assistant doesn’t take your code into account!) and flip to landscape. Now the views line up nicely. Flip back to portrait and verify that everything looks good there as well.
It works, but that was a lot of code you had to write for a layout that is pretty simple. Imagine the effort it takes for layouts that are truly complex, especially dynamic ones where the individual views change size, or the number of subviews isn’t fixed.
Now try running the app on the 3.5-inch simulator. Whoops. The positions and sizes of the views are wrong because the hardcoded coordinates in viewWillLayoutSubviews are based on the dimensions of the 4-inch phone (320×568 instead of 320×480). You could add another if-statement that checks the screen size and uses a different set of coordinates, but you can see that this approach is becoming unworkable quickly.
Another approach you could take is to make separate nibs for the portrait and landscape orientations. When the device rotates you load the views from the other nib and swap out the existing ones. But this is still a lot of work and it adds the trouble of having to maintain two nibs instead of one. This approach is quite impractical when you’re using storyboards instead of nibs.
Auto Layout to the rescue!
You will now see how to accomplish this same effect with Auto Layout. First, remove viewWillLayoutSubviews from ViewController.swift, because you’re going to do this without writing any code!
Select Main.storyboard and in the File inspector panel, check both the Use Auto Layout and Use Size Classes options to enable Auto Layout and Size Classes for this storyboard file:
A quick note on Size Classes
Size classes are a brand new and super exciting feature of iOS 8 and Xcode 6, and they make it really intuitive to only have one storyboard for universal apps. Nearly everything you see on screen can have size classes, including the screen (UIScreen), views, and your view controllers. There are two types of size classes: vertical and horizontal. Each vertical and horizontal size class can have one of three values: Regular, Compact, or Any.
The size classes correspond to the device and orientation your app is running in. For example, a portrait iPhone has a Regular height and a Compact width. The Any value is used as the generic size class value; think of it as the superclass of all the other layouts. If there is nothing defined for the size class relating to the current device and orientation, the storyboard will pull our layout from Any.
It’s easy to view and switch between size class configurations in Xcode 6. At the bottom of the storyboard, towards the middle you will see a label that says “wAny hAny“. Click on it to see the size class configuration grid:
You can move your cursor over the other boxes in the grid to see which set of squares corresponds to which size class configuration. By default you start on Any width and Any height. This is the default, generic size class configuration. Apple recommends doing all of your initial interface layout in this class for a universal app, since all size classes will pull from this one initially. Make sure “wAny hAny” is the selected size class in your storyboard.
You will notice the size of the scenes in your story board have changed to squares to reflect our generic size class configuration.
For more detail about size classes, check out our Beginning Adaptive Layout Tutorial. This tutorial will stick to the basics of Auto Layout.
Your First Auto Layout Constraints
Look at the landscape layout in the preview assistant. It now looks like this:
Let’s put Auto Layout into action. Hold down the ⌘ key while you click on the two views on the top (the green and yellow ones), so that both are selected. From Xcode’s Editor menu, select Pin\Widths Equally:
Select the same two views again and choose Editor\Pin\Horizontal Spacing. (Even though the two views appear selected after you carry out the first Pin action, do note that they are in a special layout relationship display mode. So you do have to reselect the two views.)
The storyboard now looks like this:
The orange “T-bar” shaped things represent the constraints between the views. So far you added two constraints: an Equal Widths constraint on both views (represented by the bars with the equals signs) and a Horizontal Space constraint that sits between the two views. Constraints express relationships between views and they are the primary tool you use to build layouts using Auto Layout. It might look a bit scary, but it is actually quite straightforward once you learn what it all means.
To continue building the layout for this screen, perform the following steps. Each step adds more orange T-bars. Remember to re-select the view again after adding each constraint.
For the green view on the top left, choose from the Editor\Pin menu:
- Top Space to Superview
- Leading Space to Superview
For the yellow view on the right, choose:
- Top Space to Superview
- Trailing Space to Superview
And for the big blue view at the bottom:
- Leading Space to Superview
- Trailing Space to Superview
- Bottom Space to Superview
You should now have the following constraints:
Notice that some of the T-bars are still orange. That means your layout is incomplete; Auto Layout does not have enough constraints to calculate the positions and sizes of the views. The solution is to add more constraints until they turn blue.
Hold down ⌘ and select all three views. From the Editor menu, choose Pin\Heights Equally.
Now select the top-left corner view and the bottom view (using ⌘ as before), and choose Editor\Pin\Vertical Spacing.
Interface Builder should show something like this:
The T-bars have become blue: great success! Auto Layout now has enough information to calculate a valid layout. It’s not quite right yet, because there’s that big space at the right of the screen which was made when you converted to the generic size class. Select the bottom view, then the trailing space constraint:
Open the Size inspector and change the Constant value to 20:
Do the same for the top right view as well.
Look at the layout preview, and voila! Perfect in portrait and landscape. It also doesn’t matter which simulator you run this on; the layout works fine on 3.5-inch, 4 inch, 4.7 inch and 5.5 inch devices. Add a few devices to the preview assistant and check it out. You may also notice that “iPad” is now an option – add that as well and you’ll see that your single layout is supporting every type of device!
Cool, but what exactly did you do here? Rather than requiring you to hard-code how big your views are and where they are positioned, Auto Layout lets you express how the views in your layout relate to each other and their superview.
You have put the following relationships – what are known as constraints – into the layout:
- The top-left and top-right views always have the same width (that was the first pin widths equally command).
- There is a 20-point horizontal padding between the top-left and top-right views (that was the pin horizontal spacing).
- All the views always have the same height (the pin heights equally command).
- There is a 20-point vertical padding between the two views on top and the one at the bottom (the pin vertical spacing).
- There is a 20-point margin between the views and the edges of the screen (the top, bottom, leading, and trailing space to superview constraints).
And that is enough to express to Auto Layout where it should place the views and how it should behave when the size of the screen changes.
You can see all your constraints in the Document Outline on the left. Xcode adds the section named Constraints when you enabled Auto Layout for the storyboard. If you don’t see the outline pane, then click the arrow button at the bottom of the Interface Builder window.
If you click on a constraint in the Document Outline, Interface Builder will highlight where it sits on the view by drawing a white outline around the constraint and adding a shadow to it so that it stands out:
Constraints are real objects (of class NSLayoutConstraint
) and they also have attributes. For example, select the constraint that creates the padding between the two top views (it is named “Horizontal Space (20)” but be sure to pick the correct one) and then switch to the Attributes inspector. There you can change the size of the margin by editing the Constant field.
Set it to 100 and look in the Preview Assistant. Now the margin is a lot wider:
Auto Layout is a lot more expressive than springs and struts when it comes to describing the views in your apps. In the rest of this tutorial, you will learn all about constraints and how to apply them in Interface Builder to make different kinds of layouts.
How Auto Layout Works
As you’ve seen in the test drive above, the basic tool in Auto Layout is the constraint. A constraint describes a geometric relationship between two views. For example, you might have a constraint that says:
“The right edge of label A is connected to the left edge of button B with 20 points of empty space between them.”
Auto Layout takes all of these constraints and does some mathematics to calculate the ideal positions and sizes of all your views. You no longer have to set the frames of your views yourself – Auto Layout does that for you, entirely based on the constraints you have set on those views.
Before Auto Layout, you always had to hard-code the frames of your views, either by placing them at specific coordinates in Interface Builder, by passing a rectangle into init(frame:), or by setting the view’s frame, bounds or center properties directly.
For the app that you just made, you specifically set the frames to:
You also set autosizing masks on each of these views:
That is no longer how you should think of your screen designs. With Auto Layout, all you need to do is this:
The sizes and positions of the views are no longer as important. Of course, when you drag a new button or label on to the canvas it will have a certain size and you will drop it at a certain position, but that is only a design aid that you use to tell Interface Builder where to put the constraints.
The idea behind auto layout is to simplify this where you set a few constants – such as the 20 point margin or maybe an exact width for an image – and then build the rest of the layout in a relative fashion.
Designing by Intent
The big advantage of using constraints is that you no longer have to fiddle with coordinates to get your views to appear in the proper places. Instead, you can describe to Auto Layout how the views are related to each other and Auto Layout will do all the hard work for you. This is called designing by intent.
When you design by intent, you’re expressing what you want to accomplish but not necessarily how it should be accomplished. Instead of saying: “the button’s top-left corner is at coordinates (20, 230)”, you now say:
“The button is centered vertically in its superview, and it is placed at a fixed distance from the left edge of the superview.”
Using this description, Auto Layout can automatically calculate where your button should appear, no matter how big or small that superview is.
Other examples of designing with intent (and Auto Layout can handle all of these instructions):
“These two text fields should always be the same size.”
“These two buttons should always move together.”
“These four labels should always be right-aligned.”
This makes the design of your user interfaces much more descriptive. You simply define the constraints, and the system calculates the frames for you automatically.
You saw in the first section that even a layout with just a few views needs quite a bit of work to layout properly in both orientations. With Auto Layout you can skip all that effort. If you set up your constraints properly, then the layout should work without any changes in both portrait and landscape.
Another important benefit of using Auto Layout is internationalization. Text in German, for example, is infamous for being very long and getting it to fit into your labels can be a headache. Again, Auto Layout takes all this work out of your hands, because it can automatically resize your labels based on the content they need to display – and have everything else adapt with constraints.
Adding support for German, French, or any other language is now simply a matter of setting up your constraints, translating the text, and… that’s it!
Auto Layout is not just useful for rotation; it can also easily scale your UI up and down to accommodate different screen sizes. It is no coincidence that this technology was added to iOS at the same time that the iPhone 5 and its taller screen came out, and now we have the even bigger iPhone 6 and 6 Plus!
Auto Layout makes it a lot easier to stretch your apps’ user interfaces to fill up that extra space on the larger phones. And with Dynamic Type in iOS 7 Auto Layout has become even more important. Users can now change the global text size setting — with Auto Layout this is easy to support in your own apps.
The best way to get the hang of Auto Layout is to play with it, so that’s exactly what you will do in the rest of this tutorial.
Courting constraints
Close your current project and create a new iPhone project using the Single View Application template. Name it “Constraints”.
Any new projects that you create with Xcode 6 automatically assume that you will be using Auto Layout, so you do not need to do anything special to enable it. To keep things simple though, open Main.storyboard and disable Size Classes for this project.
To start off the interface, drag a new Button onto the canvas. Notice that while you’re dragging, dashed blue lines appear. These lines are known as the guides:
There are guides around the margins of the screen, as well as in the center:
If you have used Interface Builder before, then you have no doubt seen these guides. They are helpful hints that make it easier to align your views.
Note that when you add a new object to the view, there are no constraints! But how can this work? You just learned that Auto Layout always needs enough constraints to determine the size and position of all the views, but here you have no constraints at all. Surely this is an incomplete layout?
If you don’t supply any constraints at all, Xcode automatically assigns a set of default constraints, known as the automatic constraints. It does this at compile time when your app is built, not at design time. Auto Layout since Xcode 5 works hard to stay out of your way while you’re designing your user interfaces, and that’s just how we like it.
The automatic constraints give your views a fixed size and position. In other words, the view always has the same coordinates as you see in the storyboard. This is very handy because it means you can largely ignore Auto Layout. You simply don’t add constraints if the default ones are sufficient and only create constraints for those views that need special rules.
OK, let’s play around a bit with constraints and see what they can do. Right now, the button is in the top-left corner and has no constraints. Make sure the button is aligned with the two corner guides.
Add two new constraints to the button using the Editor\Pin menu, so that it looks like this:
If you hadn’t guessed already, that is the Leading Space to Superview and the Top Space to Superview options.
All the constraints are also listed in the Document Outline pane on the left-hand side of the Interface Builder window:
There are currently two constraints: a Horizontal Space between the button and the left edge of the main view, and a Vertical Space between the button and the top edge of the main view. The relationship that is expressed by these constraints is:
“The button always sits at 20 points from the top-left corner in its superview.”
Note: These aren’t actually very useful constraints to make because they’re the same as the automatic ones. If you always want your button to be relative to the top-left corner of its superview, then you might as well not provide any constraints at all and let Xcode make them for you.
Now pick up the button and place it in the scene’s top-right corner, again against the blue guides:
Whoa, what has happened with all that angry orange? The problem here is that the size and position of the button in Interface Builder no longer correspond with the size and position that Auto Layout expects based on the constraints. This is called a misplaced view.
Run the app. The button will still appear in the top-left corner of the screen:
When it comes to Auto Layout, orange is bad. Interface Builder drew two orange boxes: one with a dashed border and one with a solid border. The dashed box displays the view’s frame according to Auto Layout. The solid orange box is the view’s frame according to how you placed it in the scene. These two should match up, but here they don’t.
How you fix this depends on what you want to achieve:
- Do you want the button to be attached to the left edge of the screen at a distance of 254 points? In that case you need to make the existing Horizontal Space constraint 234 points bigger. That’s what the orange badge with “+234″ means.
- Do you want the button to be attached to the right edge of the screen instead? Then you need to remove the existing constraint and create a new one.
Delete the Horizontal Space constraint. First select it in the canvas or in the Document Outline, and then press the Delete key on your keyboard.
Notice the orange outline around the button, and the dotted red box. The orange outline is telling you something is wrong, and the red dotted box is telling you where Auto Layout thinks this button will appear at runtime. This is happening because there are not enough constraints left to determine the complete position of the button. You still need to add a constraint for the X-position.
You may be wondering why Xcode does not add an automatic constraint for the X-position. The rule is that Xcode only creates automatic constraints if you did not set any constraints of your own. As soon as you add a single constraint, you tell Xcode that you’re now taking responsibility for this view. Xcode will no longer make any automatic constraints and expects you to add any other constraints this view needs.
Select the button and choose Editor\Pin\Trailing Space to Superview. This puts a new constraint between the right edge of the button and the right edge of the screen. This expresses the relationship:
“The button always sits at 20 points from the top-right corner in its superview.”
Run the app and rotate to landscape. Notice how the button keeps the same distance from the right screen edge:
When you place a button (or any other view) against the guides and make a constraint, you get a spacing constraint with a standard size that is defined by Apple’s iOS Human Interface Guidelines document (also known as the “HIG”). For margins around the edges, the standard size is a space of 20 points.
Now drag the button over to the left a little:
Again you get a dashed orange box because the view is misplaced. Let’s say this new button position is indeed what you want. It’s not uncommon to make a constraint and then nudge the view by a few pixels, making the orange boxes appear. One way to fix this is to remove the constraint and make a new one, but there is an easier solution.
The Editor menu has a Resolve Auto Layout Issues submenu. From that menu, choose Update Constraints. In my case, this tells Interface Builder it should make the constraint 64 points larger, as so:
Great, the T-bars turn blue again and the layout is valid. In the Document Outline, you can see that the Horizontal Space constraint no longer has a standard space:
So far you’ve played with Horizontal Space and Vertical Space constraints. There is also a “center” constraint. Drag a new Button object to the bottom center of the canvas, so that it snaps into place with the guides:
To keep the button always center-aligned with its superview, on the horizontal axis, you need to add a Center X Alignment constraint. From the Editor menu choose Align\Horizontal Center in Container. This adds a long orange line:
The line is orange because you’ve only specified what happens to the X-coordinate of the button, not its Y-coordinate. Use the Editor\Pin menu to add a Vertical Space constraint between the button and the bottom of the view. It should look like this:
In case you didn’t know how, it is the Bottom Space to Superview option. The Vertical Space constraint keeps the button away from the bottom of the view (again, using the standard margin).
Run the app and rotate it to landscape. Even in landscape mode, the button stays at the bottom center of the screen:
That’s how you express intent: “This button should always be at bottom center.” Notice that nowhere did you have to tell Interface Builder what the button’s coordinates are, only where you want it anchored in the view.
With Auto Layout, you’re no longer supposed to care about the exact coordinates of where you place your views on the canvas or what their size is. Instead, Auto Layout derives these two things from the constraints that you set.
You can see this paradigm shift in the Size inspector for the button, which is now quite different:
With Auto Layout disabled, typing into the X, Y, Width or Height fields will change the position and size of the selected view. With Auto Layout enabled you can still type new values into these fields, but if you already have constraints set on the view it may now become misplaced. You also have to update the constraints to make them match the new values.
For example, change the Width value of the button to 100. The canvas turns into something like this:
Xcode simply says, “It’s fine with me if you want the width to be 100 points but just so you know, that’s not what the constraints say.”
In this case you do want the button to be 100 points wide. There is a special type of constraint for this: the Fixed Width constraint. First press Undo so that the button is centered again and the T-bars are all blue. Select the button and choose Editor\Pin\Width. This puts a new T-bar below the button:
Select that T-bar and in the Attributes inspector change Constant to 100. This forces the button to always be 100 points wide, no matter how large or small its title. To see this a bit better you can give the button a background color:
You can also see this new Width constraint in the Document Outline on the left:
Unlike the other constraints, which are between the button and its superview, the Width constraint only applies to the button itself. You can think of this as a constraint between the button and… the button.
You may wonder why the button did not have a Width constraint before. How did Auto Layout know how wide to make the button without it?
Here’s the thing: the button itself knows how wide it must be. It calculates this based on its title text plus some padding. If you set a background image on the button, it also takes that into account.
This is known as the intrinsic content size. Not all controls have this, but many do (UILabel is another example). If a view can calculate its own preferred size, then you do not need to set specific Width or Height constraints on it. You will see more of this later.
To return the button to its optimal size, first remove the Width constraint. Then select the button and choose Size to Fit Content from the Editor menu. This restores the button’s intrinsic content size.
It takes two to tango
Guides do not appear only between a view and its superview, but also between views on the same level of the view hierarchy. To demonstrate this, drag a new button onto the canvas. If you drag this button close to the others, then their guides start to interact.
Put the new button next to the existing one so that it snaps into place:
There are quite a few dotted guidelines here. Interface Builder recognizes that these two buttons can align in different ways – at their tops, centers and baselines.
Xcode 4 would have turned one of these snapping guides into a new constraint. But since Xcode 5, if you want to have a constraint between these two buttons, you have to make it yourself. You’ve seen that you can use the Editor\Pin menu to make a constraint between two views, but there is an easier way too.
Select the new button and Ctrl-drag to the other button, like so:
When you let go of the mouse button, a popup appears. Choose the first option, Horizontal Spacing.
This creates a new constraint that looks like this:
It is orange, meaning that this button needs at least one other constraint. The size of the button is known — it uses the intrinsic content size — and there is a constraint for the button’s X-position. That leaves only the Y-position without a constraint.
Here the missing constraint is pretty easy to determine but for more complicated designs it may not always be immediately obvious. Fortunately, you don’t have to guess. Xcode has been keeping score and can tell you exactly what is missing.
There is small a red arrow in the Document Outline, next to View Controller Scene. Click that arrow to see a list of all Auto Layout issues:
Sweet! Let’s add that missing Y-position constraint. Ctrl-drag from the new button downwards to the containing view:
The popup menu has different options this time. The items in this menu depend on the context — which views are you dragging between — and the direction you moved the mouse. Choose Bottom Space to Bottom Layout Guide.
The new button now has a Vertical Space to the bottom of the screen, but also a Horizontal Space that links it with the other button. Because this space is small (only 8 points), the T-bar may be a bit hard to see, but it is definitely there.
Click on the Horizontal Space (8) constraint in the Document Outline to select it:
This particular constraint sits between the two buttons. What you’ve done here is say:
“The second button always appears on the left of the first one, no matter where the first button is positioned or how big it is.”
Select the button on the right and type something long into its label like “A longer label”. When you’re done, the button resizes to make room for the new text, and the other button shifts out of the way. After all, it is attached to the first button’s left edge, so that is exactly what you intended to happen:
Just to get a better feel for how this works, play with this some more. First give the longer label button a yellow background. Drag another button into the canvas and put it above the yellow one, so that they snap into place vertically (but don’t try to align the left edges of the two buttons):
Give the new button a background color (green) so you can more easily see its extents.
Because you snapped the two buttons together, there is now a standard space of 8 points between them that is recommended by the HIG. Turn this into a constraint by Ctrl-dragging between the two buttons. Select Vertical Spacing from the popup menu.
You are not limited to standard spacing between controls. Constraints are full-fledged objects, just like views, and therefore have attributes that you can change.
Select the Vertical Space constraint between the two buttons. You can do this in the canvas by clicking the T-bar, although that tends to be a bit finicky. By far the easiest method is to click on the constraint in the Document Outline. Once you have it selected, switch to the Attributes inspector:
Type 40 into the Constant field to change how big the constraint is.
Run the app and flip to landscape to see the effect, or add a landscape preview assistant like you did earlier:
The buttons certainly keep their vertical arrangement, but not their horizontal one! The reason should be obvious: the green button does not have a constraint for its X-position yet.
Adding a Horizontal Space from the green button to the left edge of the canvas won’t solve this problem. With such a constraint the green button always keeps the same X-coordinate, even in landscape. That doesn’t look very nice, so instead you are going to express the following intention:
“The yellow button will always be horizontally centered, and the green button will align its left edge with the left edge of the yellow button.”
You already have a constraint for the first condition, but not for the second. Interface Builder shows guides for alignment, so you can drag the top button until its left edge snaps with the yellow button:
Finally, Ctrl-drag between the two buttons and from the popup menu choose Left. This creates an alignment constraint that says: “The left edges of these two views are always aligned”. In other words, the two buttons will always have the exact same X-position. That solves the layout problem and the T-bars turn blue:
Run the app and rotate to landscape or check the preview assistant to verify that it works:
Where To Go From Here?
Now that you’ve got your first taste of Auto Layout, how do you like it? It can take a bit of getting used to, but can make your life a lot easier and your apps much more flexible!
Want to learn more? Stay tuned for part 2 of this Auto Layout tutorial, where you’ll continue playing with the buttons in Interface Builder to get a better understanding of the possibilities Auto Layout offers — and the problems you may encounter.
And best of all – you will also use Auto Layout to create a realistic layout that you may find in a real app! :]
In the meantime, if you have any questions or comments please join the forum discussion below!
Beginning Auto Layout Tutorial in Swift: Part 1/2 is a post from: Ray Wenderlich
The post Beginning Auto Layout Tutorial in Swift: Part 1/2 appeared first on Ray Wenderlich.