Placing UI Elements with Auto Layout

In iOS, our UI elements are placed onscreen with an auto layout system that lets you determine where objects should go and how big they should be based on constraints that we set on them. This allows our interfaces to adapt to being rotated between portrait and landscape, and to handle the differing screen sizes of the 3.5-inch models (iPhone 4s), 4-inch models (iPhone 5, 5c, 5s, and SE), 4.7-inch models (iPhone 6, 6s, and 7), and 5.5-inch models (iPhone 6 Plus, 6s Plus, and 7 Plus). Constraints allow us to express what matters to us—the size of components, their alignment with or distance from other components, and so forth—and to let other factors vary as needed.

In this example, we want to put the Play button at upper left, with the track title label below it, and the current time label in the upper right.

images/userinterface/xcode-autolayout-buttons.png

Interface Builder puts a set of buttons, shown here, at the bottom right of the content area to give us access to Auto Layout features. These buttons display a pop-over or pop-up menu when tapped.

images/userinterface/xcode-autolayout-buttons-updateframes.png

Update Frames: This adjusts a view’s position and/or size so it matches its constraints.

images/userinterface/xcode-autolayout-buttons-stack.png

Stack button: This button embeds one or more selected views into a stack view—a container for other views (and something we’ll explore in a later chapter).

images/userinterface/xcode-autolayout-buttons-align.png

Align pop-over: This lets us create constraints that align a view’s edges or horizontal or vertical center with another view, or horizontally or vertically center it within its containing view (its superview).

images/userinterface/xcode-autolayout-buttons-pin.png

Pin pop-over: This lets us create constraints that specify a fixed value for spacing from one or more edges to another view (possibly the superview), and/or a fixed width or height.

images/userinterface/xcode-autolayout-buttons-resolve.png

Resolve menu: The options here update the values of existing constraints to match the current position and size, or create any constraints that appear to be missing. We can also clear all constraints and start over with this menu.

With these tools, we can get our layout to look correct on any device and orientation, using three simple rules:

  1. The Play button will always be at upper left (with a little margin padding from the top and left sides of the screen).

  2. The current time label (“0:00”) will be vertically aligned with the Play button, meaning it’s always at the same height, and it will have a fixed distance from the right side. This will keep it consistently in the upper right.

  3. The track title label will be left-aligned with the Play button, meaning their left sides will line up, while being placed a fixed distance below the button.

Laying Out the Play Button

We’ll start with the Play button. Select it in the storyboard, and then click the Pin button (the third from left, easily recognized because it looks like a TIE Fighter from Star Wars.) This shows the pop-over in the following figure.

images/userinterface/xcode-autolayout-play-button-pin.png

The top portion of this pop-over lets us “pin” exact distances from other UI components, or set exact values for width and height. In the top portion, our button is represented as a small square surrounded by braces on its top, bottom, left, and right. At the end of each brace, there’s a combo-box menu saying which other component we want to set our spacing relative to, and how many points of distance we want.

Just below the braces, there’s a check box called “Constrain to margins.” Click that on, and then set the left and top distances to 0, and click each brace to make sure it’s solid red. For the top distance, use the menu to verify that it says “Top Layout Guide” rather than “View,” since this will help us deal with situations that take away space at the top of our UI (navigation bars, double-height status bars when a navigation app is running, etc.). If you don’t see “Top Layout Guide” as an option, drag the button further down in the view and try again. When everything is set, click the Add 2 Constraints button at the bottom of the pop-over.

images/userinterface/xcode-autolayout-misplaced-box.png

With any luck, our button will snap to its proper position at the upper left. On the other hand, this may cause an orange box to appear near our button, usually with orange braces that show numbers, when the label is selected. This is telling us that the label is currently misplaced relative to the constraints we just asked for. The box shows where the button should be if the constraints are applied, and any numeric values tell us how far off we are. Click the Update Frames button (the leftmost of the five), or use its keyboard shortcut =. This causes the button to jump to its correct position.

Laying Out the Time Label

Next up, let’s fix the current time label. Select it and bring up the Pin menu again. We only want to pin one value this time: a distance of 0 to the margin on its right side. This will put a red box around the label when selected (as well as a red-arrow mini button next to View Controller Scene in the Document Outline pane). Red means that we don’t have enough constraints. We’ve told Auto Layout where to put it horizontally—along the right edge—but not vertically.

We could pin the label to the Top Layout Guide, like we did with the Play button, but it would be nicer to align it to the Play button itself. That way, if we ever move the Play button, the current time label can move with it. So what we need is a constraint that applies to both of these components. Select both the Play button and the current time label via Shift-click, Command-click, or dragging a selection box over both of them (but not the track title label). Bring up the Alignment menu, shown in the following figure.

The Alignment menu lets us create constraints between the two selected items, along with numeric values to “tweak” the alignment if needed. In this case, we want something that will line them up horizontally. Depending on relative sizes, aligning their tops or bottoms could work, and for two labels aligning baselines is often a good choice, but the safe option for now is to click the Vertical Centers button. So do that, and click the Add 1 Constraint button. Then, as before, use the Update Frames button to put the components in the right place, in accordance with our new constraints.

images/userinterface/xcode-autolayout-play-button-align.png

If we select either or both of these objects, they’re now shown with blue lines indicating their layout constraints. Blue is good; it means there’s no ambiguity, that there are enough constraints, and that they have been applied correctly.

Laying Out the Title Label

Let’s finish up with the track title label. Make sure it’s a safe distance below the Play button, select it, and bring up the Pin menu. We want it a fixed distance below the Play button, so put 8 for the top distance constraint, and check the pop-up menu to make sure that the Play button is checked (meaning that the 8 points will be relative to the Play button, not the Top Layout Guide or the parent view). Click Add 1 Constraint.

As before with the current time label, we are now under-constrained—we have a vertical position based on the Play button and its layout, but not a horizontal position. Again, we want to align this label to the Play button. Select both the track title label and the Play button, and bring up the Alignment menu. This time, choose Leading Edges. In this case, “leading” means the left side in left-to-right systems (like English), and right in right-to-left systems. Click Add 1 Constraint, and then clean up with Update Frames as before.

This looks fine, and has blue lines to indicate the title label has enough constraints, but there’s actually a subtle bug. If we had a really long title, there is nothing to prevent the label from going off the right side of the screen. Often with labels, we want constraints on both sides to prevent this. Select the title label, bring up the Pin menu, and add a right constraint of 0, with Constrain to Margins still selected. This will stretch the handles all the way over to the right side, but the text should remain left-aligned inside the label.

Now we have a full set of constraints for each item in our UI. Let’s try it out. Below View As, select the landscape orientation. The current time label will keep at the upper right as seen in the figure. The layout will also maintain its spacing and alignment to the edges when different iPhone models are selected from the Device section. And we can run the app again in the simulator and rotate the simulated device to our hearts’ content.

images/userinterface/xcode-button-and-labels-landscape-with-autolayout.png

Now imagine if we had to explicitly set each object’s position and size in code: it would be a nightmare! With Auto Layout, we get to describe size, shape, and position with constraints, whereas, if we build our UI in code, we would be doing a bunch of math to set the position and size, using logic like “subtract the label’s width from the superview’s width minus the margin.” For complex layouts on devices with different sizes and shapes, all of which can be rotated at any time, Auto Layout ends up being both easier and more dependable.

As layouts get more complex, Auto Layout has advanced features that we can use to help resolve complex situations. Each constraint has a priority value, so if we get into a case where conflicting constraints create an ambiguity, the Auto Layout system can compare priorities as a tiebreaker. It’s also possible to create constraints in code, so if we did have to create an arbitrary number of views at runtime, we could still use Auto Layout on them and not resort to doing our own math to position them. It’s a sophisticated system, but for starters, it’s enough to just do the pinning and aligning supported by the storyboard UI.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset