By far, the most productive and intuitive way to work with constraints is using the Auto Layout features of Interface Builder. Not only does this avoid the necessity to write time-consuming code (though for complex layout requirements, some code will be inevitable), but it also provides instant visual feedback on constraints as they are configured.
Within this chapter, a simple example will be used to demonstrate the effectiveness of Auto Layout, together with an in-depth look at the Auto Layout features of Interface Builder. The chapter will then demonstrate the concepts of content hugging and constraint priorities.
An Example of Auto Layout in Action
Before digging deeper into the Auto Layout features of Interface Builder, the first step in this chapter will be to demonstrate the basic concept of Auto Layout quickly. Begin, therefore, by creating a new Xcode project using the iOS App template with the Swift and Storyboard options selected, entering AutoLayoutExample as the product name.
Working with Constraints
Although Auto Layout is enabled by default, the Interface Builder tool does not automatically apply any default constraints as views are added to the layout. Views are instead positioned using absolute x and y coordinates. To see this in action, drag a label view from the Library and position it towards the bottom of the view in the horizontal center of the view canvas so the vertical blue guideline appears, indicating that it is centered before dropping the view into place. The location of the view has just been defined using hard-coded absolute x and y coordinates on the screen. As far as the view is concerned, the label is positioned perfectly as long as the device remains in portrait orientation:
A problem arises, however, when the device rotates to landscape orientation. This can be demonstrated by compiling and running the app on a physical iPhone or iPad device or iOS Simulator in the usual way.
Alternatively, the effect of an orientation change can be tested within the Interface Builder environment. To rotate to landscape mode, click on the Orientation entry located in the status bar positioned along the bottom edge of the Interface Builder panel as highlighted in Figure 19-2 below:
As illustrated in Figure 19-3, the label is no longer visible with the device in landscape orientation. This is because it remains positioned at the same geographical coordinates in relation to the parent view, which, in landscape orientation, is outside the visible bounds of the parent view as shown in Figure 19-3:
Similar problems can occur when the app runs on different sizes of iPhone devices which can be tested by clicking on the Devices button () and selecting different device models:
For example, return the orientation to portrait mode and switch between iPhone 14 and iPhone SE (1st Generation). Note that the label is again positioned incorrectly on larger and smaller display form factors. Clearly, layout is essential for handling device orientation and ensuring correct user interface appearance on different device models.
Before the introduction of Auto Layout, options to address this would have either involved using springs and struts or writing code to detect the device’s rotation and moving the label to the new location on the screen. Now, however, the problem can be solved using Auto Layout.
Xcode provides several different ways to add constraints to a layout. These options will be covered later in this chapter, but by far, the easiest is to use the Auto Layout toolbar. With the Label selected, click on the Add New Constraints menu (Figure 19-5):
The goal for this example is to add a constraint to the label such that the bottom edge of the label is always positioned the same distance from the bottom of the containing superview, and so that it remains centered horizontally. To do this, we are first interested in this panel’s Spacing to nearest neighbor section. This visualizes the view in question (in this case, the label) represented by the square in the middle. Extending from each square side are faded and dotted I-beam icons connecting with fields containing values. The fact that the I-beams are dotted and faded indicates that these constraints have not been set. The values indicate the current distances within the layout of the corresponding side to the nearest neighbor. The “nearest neighbor” will either be the nearest view to that side of the selected view or the corresponding side of the superview.
Select the constraint I-beam icon located beneath the view so that it appears in solid red (as shown in Figure 196) to indicate that the constraint is now set before clicking on the Add 1 Constraint button to add the constraint to the view.
Having added a constraint, Auto Layout now knows that the bottom edge of the label must always be positioned a fixed distance from the bottom edge of the containing superview. However, the layout is still missing a constraint to designate the horizontal position of the label in the superview. One way to add this constraint is to use the Align menu. With the label still selected in the view canvas and the Align menu panel displayed, enable the checkbox next to the Horizontally in Container property (Figure 19-7). Since no offset from the center is required, leave the offset value at 0.
With the constraint appropriately configured, click the Add 1 Constraint button to add the constraint to the view.
Having configured some constraints, rotate the orientation once again, noting that the label is visible and positioned sensibly. Testing different display form factors should also demonstrate that the constraints work to keep the label correctly positioned for different devices.
In this example, only a small subset of the Auto Layout features provided by Xcode has been used. Xcode provides a wide range of options and visual cues to ease creating Auto Layout constraints.
The Auto Layout Features of Interface Builder
Several features are provided in Xcode to assist in implementing Auto Layout-based constraints. This section will present a guided tour of many of these features.
Suggested Constraints
When objects are added to a layout canvas, Interface Builder does not implement any default constraints on those views leaving the developer to add constraints as needed. There is, however, the option to have Interface Builder apply suggested constraints. When this option is used, Interface Builder will apply what it believes to be the correct constraints for the layout based on the positioning of the views. Suggested constraints can be added to the currently selected view objects or an entire scene layout.
In situations where constraints are missing from a layout resulting in warnings, Interface Builder also provides the option to automatically add the constraints it believes are missing.
The options to perform these tasks are accessed via the toolbar’s Resolve Auto Layout Issues menu, as illustrated in Figure 19-9.
The menu’s top section represents tasks that relate to the currently selected views in the canvas, while the options in the lower section apply to all views in the currently selected view controller scene.
Most of the time, the suggested constraints will exactly match the required layout behavior, and occasionally, the suggested constraints will be incorrect. Most of the time, however, the suggested constraints provide an excellent starting point for implementing Auto Layout. For example, a typical process for designing a user interface might involve positioning the views by dragging and dropping them into place, applying suggested constraints, and then editing and fine-tuning those constraints to perfect the layout.
To see suggested constraints in action, select the label view in the AutoLayoutExample project and select the Clear Constraints option from the Resolve Auto Layout Issues menu. There are no constraints in the layout at this point, and the old positioning problem appears when the view is rotated. With the label still selected, choose the Reset to Suggested Constraints menu option listed under Selected Views. Reviewing the view canvas and orientation change should demonstrate that Interface Builder has suggested and applied the same constraints we previously added manually.
Visual Cues
Interface Builder includes several visual cues in the layout canvas to highlight the constraints currently configured on a view and to draw attention to areas where problems exist. When a view is selected within the layout canvas, the constraints that reference that view will be represented visually. Consider, for example, the label view created in our AutoLayoutExample app. When selected in the canvas, several additional lines appear, as shown in Figure 19-10:
The vertical line that runs through the center of the label indicates the presence of a constraint that positions the label in the horizontal center of the parent view (analogous to the NSLayoutConstraint.Attribute.centerX attribute). If expressed as an equation, therefore, this would read as:
label.NSLayoutConstraint.Attribute.centerX = superview.NSLayoutConstraint.Attribute.centerX
The I-beam line running from the bottom edge of the label view to the bottom edge of the parent view indicates that a vertical space constraint is in place between the label and the safe area. The absence of additional visual information on the line indicates that this is an equality constraint. Figure 19-11 shows an example of a “greater than or equal to” horizontal constraint between two button views:
The horizontal line running beneath the Button label text indicates that constraints are in place to horizontally align the content baseline (represented by NSLayoutConstraint.Attribute.baseline) of the two buttons.
Width constraints are indicated by an I-beam line running parallel to the edge of the view in the corresponding dimension. The text view object in Figure 19-12, for example, has a “greater than or equal to” width constraint configured:
Highlighting Constraint Problems
Interface Builder also uses a range of visual cues and decorations to indicate that constraints are either missing, ambiguous, or in conflict. For example, valid and complete Auto Layout configurations are drawn using blue lines. However, when part of a layout is ambiguous, the constraint lines are orange.
Ambiguity typically occurs when a constraint is missing. Take, for example, the label view used earlier in the chapter. If only the horizontal center constraint is set, that constraint line will appear in orange because Auto Layout does not know where to position the view in the vertical plane. However, once the second constraint is set between the bottom edge of the label and the bottom of the superview, the constraint line will turn blue to indicate that the layout is no longer ambiguous.
Red constraint lines are used to indicate that constraints conflict. Consider, for example, a view object on which two width constraints have been configured, each for a different width value. The Auto Layout system categorizes such a situation as a constraint conflict, and Interface Builder draws the offending constraint lines on the layout canvas in red. Figure 19-13, for example, illustrates a conflict where one constraint is attempting to set the width of a view to 110 points while a second constraint dictates that the width must be greater than or equal to 120 points:
The layout canvas will dynamically update the positions and sizes of the views that make up a user interface as constraints are added. Under certain circumstances, however, it is possible to have constraints configured that will result in layout behavior different from that currently displayed within the canvas. When such a situation arises, Interface Builder will draw a dotted orange outline indicating the actual size and location of the frame for the currently selected item. This is, perhaps, best demonstrated with an example. Within the AutoLayoutExample project, add a Text Field object to the layout so that it is positioned near the top of the view and to the right of the horizontal center, as shown in Figure 19-14:
Select the new Text Field object and, using the Add New Constraints menu, establish a Spacing to nearest neighbor constraint from the top of the text view to the top of the safe area. Then, using the Align menu, add another constraint that aligns the view with the horizontal center in the container. Having established these constraints, review the layout within the view canvas and note that the Text View is positioned in compliance with the newly added constraints.
Click and drag the Text View to no longer be in the correct position. As outlined in Figure 19-15, the horizontal center constraint appears in orange with a number assigned to it. A dotted box also appears on this line, level with the Text View object. Interface Builder is attempting to warn us that the size and position of this view will resemble the dotted orange box at run time and not the size and position currently shown for the view in the canvas. The number on the horizontal center constraint tells us that the object’s horizontal position will be a specific number of points to the left of the current position.
To reset the view to the size and position dictated by the constraints so that the canvas matches the runtime layout, select the Text View object and click on the Update Frames button located in the Interface Builder status bar, as highlighted in Figure 19-16:
The canvas will subsequently update to reflect the correct layout appearance (Figure 19-17):
If, on the other hand, the text view had been positioned correctly (in other words, the visual position was correct, but the constraints were wrong), the current constraints could have been adjusted to match the actual position of the view using the Update Constraint Constants option of the Resolve Auto Layout Issues menu.
Viewing, Editing, and Deleting Constraints
All of the constraints currently set on the views of a user interface may be viewed at any time from within the Document Outline panel positioned to the left of the Interface Builder canvas area. Hidden by default, this panel can be displayed by clicking on the button in the bottom left-hand corner of the storyboard canvas (marked by the arrow in Figure 19-18).
Within this outline, a category listed as Constraints will be present, which, when unfolded, will list all of the constraints currently configured for the layout. Note that when more than one container view is present in the view hierarchy, there will be a separate constraints list for each one. Figure 19-19, for example, lists the constraints for the user interface represented in Figure 19-10 above:
Also listed within the outline is the safe area belonging to the UIView component. As indicated, the bottom of the label is constrained to the bottom edge of the safe area.
As each constraint is selected from the outline list, the corresponding visual cue element will highlight within the layout canvas.
The details of a particular constraint may be viewed and edited at any time using various methods. For example, one method is to double-click on the constraint line in the canvas to display a constraint editing panel:
Another option is to select the constraint from the layout canvas or the Document Outline panel. Once selected, display the Attributes Inspector in the Utilities panel (View -> Utilities -> Show Attributes Inspector) to view and edit the properties of the constraint. For example, Figure 19-20 illustrates the settings for an equality spacing constraint.
A listing of the constraints associated with a specific view can be obtained by selecting that view in the layout canvas and displaying the Size Inspector in the Utilities panel. Figure 19-21, for example, lists two constraints that reference the currently selected view. Clicking the edit button on any constraint will provide options to edit the constraint properties. In addition, constraints can be removed by selecting them in the layout canvas and pressing the keyboard Delete key.
Creating New Constraints in Interface Builder
New user constraints can be created in Interface Builder using a variety of approaches, keeping in mind that constraints can relate to more than one view at a time. For example, to configure an alignment constraint, all of the views to be included in the alignment operation must be selected before creating the constraints.
As demonstrated earlier in this chapter, one of the easiest ways is to use the various options in the toolbar in the bottom right-hand corner of the storyboard canvas.
Another helpful option is simply to Ctrl-click within a view and then drag the resulting line outside the view’s boundary. A context menu will appear on releasing the line, providing constraint options. The menu options will depend on the direction in which the line was dragged. If the line is dragged downwards, the menu will include options to add a constraint to the bottom of the view or to center vertically within the container. On the other hand, Dragging horizontally provides the option to attach to the corresponding edge of the safe area or to center horizontally. Dragging the line to another view in the canvas will provide options (Figure 19-23) to set up spacing, alignment, and size equality constraints between those views.
Adding Aspect Ratio Constraints
The height and width of a view can be constrained to retain the aspect ratio by Ctrl-clicking in the view, dragging diagonally, and then releasing. In the resulting menu, selecting Aspect Ratio ensures that the current aspect ratio will be preserved regardless of whether the view shrinks or grows.
Resolving Auto Layout Problems
Another advantage of implementing Auto Layout constraints in Xcode is that several features are available in Xcode to assist in resolving problems.
In the first instance, descriptions of current issues can be obtained by clicking on the yellow warning triangle in the top right-hand corner of the canvas area:
Solutions to some problems may be implemented by using the options in the Resolve Auto Layout Issues menu to perform tasks such as automatically adding missing or resetting to suggested constraints.
More detailed resolution options are available from within the document outline panel. When issues need to be resolved, a red circle with a white arrow appears next to the corresponding view controller name in the outline panel, as shown in Figure 19-26:
Clicking on the red circle displays all current layout issues listed by category:
Hovering over a category title (for example, Missing Constraints) will display an information symbol that, when clicked, will display a detailed description of the problem type.
Clicking on the error or warning symbol will display a panel providing one or more possible solutions together with a button to apply the selected change:
Summary
This chapter has looked at a very simplistic example of the benefits of using Auto Layout in iOS user interface design. The remainder of the chapter has been dedicated to providing an overview of the Auto Layout features available in Interface Builder.