Arguably one of the most important parts of designing the user interface for an app involves getting the layout correct. In an ideal world, designing a layout would consist of dragging view objects to the desired location on the screen and fixing them at these positions using absolute X and Y screen coordinates. However, in reality, the world of iOS devices is more complex than that, and a layout must be able to adapt to variables such as the device rotating between portrait and landscape modes, dynamic changes to content, and differences in screen resolution and size.
Before the release of iOS 6, layout handling involved using a concept referred to as autosizing. Autosizing involves using a series of “springs” and “struts” to define, on a view-by-view basis, how a subview will be resized and positioned relative to the superview in which it is contained. Limitations of autosizing, however, typically meant that considerable amounts of coding were required to augment the autosizing in response to orientation or other changes.
One of the most significant features in iOS 6 was the introduction of Auto Layout, which has continued to evolve with the release of subsequent iOS versions. Auto Layout is an extensive subject area allowing layouts of just about any level of flexibility and complexity to be created once the necessary skills have been learned.
The goal of this and subsequent chapters will be to introduce the basic concepts of Auto Layout, work through some demonstrative examples and provide a basis to continue learning about Auto Layout as your app design needs evolve. Auto Layout introduces a lot of new concepts and can, initially, seem a little overwhelming. By the end of this sequence of chapters, however, it should be more apparent how the pieces fit together to provide a powerful and flexible layout management system for iOS-based user interfaces.
An Overview of Auto Layout
The purpose of Auto Layout is to allow the developer to describe the behavior required from the views in a layout independent of the device screen size and orientation. This behavior is implemented by creating constraints on the views that comprise a user interface screen. A button view, for example, might have a constraint that tells the system that it is to be positioned in the horizontal center of its superview. A second constraint might also declare that the bottom edge of the button should be positioned a fixed distance from the bottom edge of the superview. Having set these constraints, no matter what happens to the superview, the button will always be centered horizontally and a fixed distance from the bottom edge.
Unlike autosizing, Auto Layout allows constraints to be declared between a subview and superview and between subviews. Auto Layout, for example, would allow a constraint to be configured such that two button views are always positioned a specific distance apart from each other regardless of changes in size and orientation of the superview. Constraints can also be configured to cross superview boundaries to allow, for example, two views with different superviews (though on the same screen) to be aligned. This is a concept referred to as cross-view hierarchy constraints.
Constraints can also be explicit or variable (otherwise referred to in Auto Layout terminology as equal or unequal). Take, for example, a width constraint on a label object. An explicit constraint could be declared to fix the width of the label at 70 points. This might be represented as a constraint equation that reads as follows:
myLabel.width = 70Code language: Swift (swift)
However, this explicit width setting might become problematic if the label is required to display dynamic content. For example, an attempt to display text on the label that requires a greater width will result in the content being clipped.
Constraints can, however, be declared using less than, equal to, greater than, or equal to controls. For example, the width of a label could be constrained to any width as long as it is less than or equal to 800:
myLabel.width <= 800Code language: Swift (swift)
The label is now permitted to grow in width up to the specified limit, allowing longer content to be displayed without clipping.
Auto Layout constraints are by nature interdependent. As such, situations can arise where a constraint on one view competes with a constraint on another view to which it is connected. In such situations, it may be necessary to make one constraint stronger and the other weaker to provide the system with a way of arriving at a layout solution. This is achieved by assigning priorities to constraints.
Priorities are assigned on a scale of 0 to 1000, with 1000 representing a required constraint and lower numbers equating to optional constraints. When faced with a decision between the needs of a required constraint and an optional constraint, the system will meet the needs of the required constraint exactly while attempting to get as close as possible to those of the optional constraint. In the case of two optional constraints, the needs of the constraint with the higher priority will be addressed before those of the lower.
When working with constraints, it is important to be aware that constraints operate on the content of a view, not the frame in which a view is displayed. This content is referred to as the alignment rect of the view. Alignment constraints, such as those that cause the center of one view to align with that of another, will do so based on the alignment rects of the views, disregarding any padding that may have been configured for the frame of the view.
Intrinsic Content Size
Some views also have what is known as an intrinsic content size. This is the preferred size that a view believes it needs to be to display its content to the user. A Button view, for example, will have an intrinsic content size in terms of height and width based primarily on the text or image it is required to display and internal rules on the margins that should be placed around that content. When a view has an intrinsic content size, Auto Layout will automatically assign two constraints for each dimension for which the view has indicated an intrinsic content size preference (i.e., height and/or width). One constraint is intended to prevent the view’s size from becoming larger than the size of the content (otherwise known as the content hugging constraint). The other constraint is intended to prevent the view from being sized smaller than the content (referred to as the compression resistance constraint).
Content Hugging and Compression Resistance Priorities
The resizing behavior of a view with an intrinsic content size can be controlled by specifying compression resistance and content hugging priorities. For example, a view with high compression resistance and low content hugging priority will be allowed to grow but will resist shrinking in the corresponding dimension. Similarly, a high compression resistance and a high content hugging priority will cause the view to resist any resizing, keeping the view as close as possible to its intrinsic content size.
Safe Area Layout Guide
In addition to the views that comprise the layout, a screen may also contain navigation and tab bars at the top and bottom of the screen. If the layout is designed to use the full screen height, there is a risk that some views will be obscured by navigation and tab bars. To avoid this problem, UIView provides a safe area layout guide for constrained views. Constraining views to the safe area instead of the outer edges of the parent UIView ensures that the views are not obscured by title and tab bars. For example, the screen in Figure 18-1 includes both navigation and tab bars. The dotted line represents the safe area layout guide to which the top edge of the Button and bottom edge of the Label has been constrained:
Three Ways to Create Constraints
There are three ways in which constraints in a user interface layout can be created:
- Interface Builder – Interface Builder has been modified extensively to support the visual implementation of Auto Layout constraints in user interface designs. Examples of using this approach are covered in the Working with iOS 16 Auto Layout Constraints in Interface Builder and Implementing Cross-Hierarchy Auto Layout Constraints in iOS 16 chapters of this book.
- Visual Format Language – The visual format language defines a syntax that allows constraints to be declared using a sequence of ASCII characters that visually approximate the nature of the constraint being created to make constraints in code both easier to write and understand. Use of the visual format language is documented in the chapter entitled Understanding the iOS 16 Auto Layout Visual Format Language.
- Writing API code – This approach involves directly writing code to create constraints using the standard programming API calls. This topic is covered in Implementing iOS 16 Auto Layout Constraints in Code.
Wherever possible, Interface Builder is the recommended approach to creating constraints. When creating constraints in code, the visual format language is generally recommended over the API-based approach.
Constraints in More Detail
A constraint is created as an instance of the NSLayoutConstraint class, which, having been created, is then added to a view. The rules for a constraint can generally be represented as an equation, the most complex form of which can be described as follows:
view1.attribute = multiplier * view2.attribute2 + constantCode language: Swift (swift)
The above equation establishes a constraint relationship between view1 and view2, respectively. In each case, an attribute is targeted by the constraint. Attributes are represented by NSLayoutConstraint.Attribute.<name> constants where <name> is one of several options, including left, right, top, bottom, leading, trailing, width, height, centerX, centerY, and baseline (i.e., NSLayoutConstraint.Attribute.width). The multiplier and constant elements are floating point values that modify the constraint.
A simple constraint that dictates that view1 and view2 should, for example, be the same width would be represented using the following equation:
view1.width = view2.widthCode language: Swift (swift)
Similarly, the equation for a constraint to align the horizontal center of view1 with the horizontal center of view2 would read as follows:
view1.centerX = view2.centerXCode language: Swift (swift)
A slightly more complex constraint to position view1 so that its bottom edge is positioned a distance of 20 points above the bottom edge of view2 would be expressed as follows:
view1.bottom = view2.bottom – 20Code language: Swift (swift)
The following constraint equation specifies that view1 is to be twice the width of view2 minus a width of 30 points:
view1.width = view2.width * 2 - 30Code language: Swift (swift)
So far, the examples have focused on equality. As previously discussed, constraints also support inequality through <= and >= operators. For example:
view1.width >= 100Code language: Swift (swift)
A constraint based on the above equation would limit the width of view1 to any value greater than or equal to 100.
The reason for representing constraints in equations is less apparent when working with constraints within Interface Builder. Still, it will become invaluable when using the API or the visual format language to set constraints in code.
Auto Layout uses constraints to descriptively express a user interface’s geometric properties, behavior, and view relationships.
Constraints can be created using Interface Builder or in code using either the visual format language or the standard SDK API calls of the NSLayoutConstraint class.
Constraints are typically expressed using a linear equation, an understanding of which will be particularly beneficial when working with constraints in code.
Having covered the basic concepts of Auto Layout, the next chapter will introduce the creation and management of constraints within Interface Builder.