跳转至

3 Stack View

UIStackView is a smart view container that intelligently arranges its subviews. Using stack views, you can create adaptive layouts with fewer constraints because most of the heavy layout work is done for you. In fact, Apple recommends that developers use stack views over manual constraints where possible.

To effectively use stack views, you need to familiarize yourself with their behavior. You need to understand how a stack view decides to align, distribute, space, size and position its subviews. The configurable stack view properties determine these factors.

In this chapter, you’ll learn about the following stack view topics:

  • Embedding views inside a stack view.
  • Adding constraints to a stack view.
  • Aligning and distributing views within a stack view.
  • Nesting stack views.
  • Deciding when and when not to use stack views.

These topics will help you understand how stack views operate. By the end of this chapter, you’ll have gained a solid foundation of stack views and be able to implement adaptive layouts with stack views in your projects.

Implementing a vertical stack view

Adding a stack view onto a view controller’s view using Interface Builder is similar to adding any other standard view object: You drag and drop a stack view object from the Object Library onto a view controller’s view. However, instead of doing that here, you’ll learn how to embed existing views into a vertical stack view.

First, open the starter project. Then, in the Storyboards group, open Profile.storyboard.

In the editor’s document outline, use Command-click to select the Profile Image Viewand the Full Name Label.

At the bottom of the editor, click Embed In.

img

From the dialog, select Stack View.

img

In the document outline, you’ll see a stack view with the profile image view and full name label embedded. You may notice that some constraints are missing in the embedded views. For instance, the constraint that spaces the profile image view’s bottom edge and the full name label’s top edge.

Your next task is to add constraints to the stack view.

Adding constraints to a stack view

Select the Profile Image View and open the Size inspector. Use Shift-click to select all of the view’s constraints, then click delete to delete the constraints.

Now, add the following constraints to the Profile Image View:

  • 160 width with a 750 priority.
  • 1:1 multiplier for a width to height aspect ratio.

You set the width constraint’s priority in the constraint details panel.

img

The 160 @ 750 width constraint allows the profile image view to get as close to 160 as possible. Instead of using a required constraint priority, you set a high constraint priority, which enables Auto Layout to ignore this constraint and satisfy a different constraint if necessary.

For example, suppose the full name label’s intrinsic content width is greater than 160 points due to an increase in font size or longer text; the full name label’s width will take precedence over the profile image view width. If the full name label’s width is 184, the layout engine is permitted to break the profile image view width.

The aspect ratio constraint keeps the width and height of the profile image view equal to each other.

In the document outline, select Stack View and add the following constraints:

  • 24 points spacing between stack view’s top edge and Safe Area’s top edge.
  • 20 points spacing between stack view’s leading edge and its superview’s leading edge. Set the constraint relation to greater than or equal to.
  • Align horizontally in container’s center.

The top constraint sets the y position of the stack view. The leading constraints add limitations to how far the stack view can expand horizontally. The center horizontally alignment constraint ensures the stack view always centers in the view.

The stack view’s subviews determine the stack view’s width. Between the label width and the image view width, the view with the larger width determines the stack view’s width. The stack view’s width also determines the stack view’s x position.

The stack view’s subviews and spacings between subviews determine the stack view’s height. The stack view now fulfills the Auto Layout position and size requirements.

Build and run, and you’ll see something like this:

img

Embedding views into a horizontal stack view

A stack view distributes its views in one of two axes: horizontal or vertical. In this section, you’ll implement a stack view that distributes its subviews on the horizontal axis. For this exercise, you’ll create a horizontal stack view with three embedded buttons.

In the main storyboard, drag three buttons onto the profile view controller’s view with the following titles:

  • Message
  • Call
  • Email

Your view will look like this:

img

Select all three buttons and embed them into a stack view. Once you embed the buttons, you’ll see the following in the editor:

img

Based on the initial positioning of the buttons, you’ll notice that the stack view distributes its subviews horizontally within Interface Builder. This is one of the smart adjustments and added benefits of using Interface Builder.

With stack views, you can typically add or remove subviews without adding additional constraint configurations. For example, imagine one day that you want to add a new button between the message and call buttons. Using a stack view, you simply drag and drop a new button between the message and call buttons. From there, the stack view automatically handles the layout.

With manual constraints, you’d typically need to remove and reconfigure the old view constraints and configure the new view’s constraints. This is extraneous work, especially when stack views are available at your disposal.

There are still layout edge cases you may need to handle, but for the most part, your layout is good to go.

Alignment and distribution

You’re ready to learn about stack view’s alignment and distribution properties.

Select the horizontal stack view. In the Attributes inspector, you’ll see the alignment and distribution properties. These are two of stack view’s magical properties for automatic layout of the subviews, and in this section, you’ll take a deeper dive into how each one works.

Alignment

Alignment defines the stack view’s subviews layout arrangement perpendicular to the stack view’s axis. The alignment property values are different for a horizontal stack view versus a vertical stack view. You won’t implement all of the possible property values in this book; however, you’ll get to see the effect each property has on a stack view.

First up, you’ll look at a stack view’s alignment properties on the horizontal axis. When a stack view’s axis is horizontal, there are six possible values: fill, top, bottom, center, first baseline and last baseline.

Horizontal axis fill alignment

In a horizontal stack view, a fill alignment makes the views take up all of the vertical space inside the stack view.

Whenever you’re setting up a superview/subview relationship, you can take the approach to define external constraints to set the size of the superview and then cascade those down to the subviews. Alternatively, you can let the size of the subviews help dictate the size of the superview. The same is true of stack views.

In a horizontal stack view, if no external constraints are setting the height, the view with the greatest height determines the stack view’s height.

With the buttons embedded into the stack view, the stack view looks like this:

img

In this case, the intrinsic size of the buttons set the height of the stack view. For more on intrinsic size, see Chapter 8, “Content Hugging and Compression Resistance Priorities.” You can change the intrinsic height of the message button by adding two additional lines to its title. Doing so causes the stack view looks like this:

img

Without the additional line spacings, but with a height constraint of 50 on the email button, the stack view looks like this:

img

In each case, the height of the tallest view determines the height of the stack view; and because the alignment is fill, each other view in the stack view matches that height.

Horizontal axis top, bottom and center alignments

The top and bottom alignment properties arrange the subviews toward the top and bottom edge of the stack view, respectively. The center alignment property arranges the subviews directly in the middle of the stack view’s axis.

If each of the subviews is the same height, a stack view with top, bottom or center alignments looks the same as a stack view with fill alignment:

img

However, when the buttons are different heights, you can see the difference each of these settings makes.

Here, the stack view alignment is set to top:

img

With the alignment set to bottom, the stack view subviews are lined up at the bottom:

img

With the alignment set to center, each subview is centered vertically in the stack view:

img

The last two options are the baseline alignments.

Horizontal axis first baseline and last baseline alignments

Imagine a yellow notepad. When you write on the notepad, you have lines on which to write your letters. These lines help you write in a straight line and mitigate the chances of letters moving in all kinds of directions.

Here’s an example to help visualize a baseline:

img

With UI objects such as a label, you can have multiple lines of text. When you have two labels in a horizontal stack view, you can decide whether you want the labels to align their first or last line of texts.

The following is an example of a stack view with two labels. The left-hand side label is three lines of text. The right-hand side label is one line of text.

Here, the stack view alignment is set to first baseline:

img

Here, the stack view alignment is set to last baseline:

img

Here’s the stack view with the action buttons and its alignment set to first baseline:

img

The stack view looks the same as the fill alignment configuration.

To see how the baseline alignment works, give the buttons different heights. This is how the stack view subviews look with the alignment set to the first baseline:

img

With the alignment set to the last baseline, the stack view subviews arrange like so:

img

Vertical axis alignments

The alignment options for a vertical stack view are: leading, trailing, fill and center.

If you reconfigure the stack view with the action buttons as a vertical stack view, leading alignment looks like this:

img

Trailing alignment, like this:

img

The vertical axis alignments do not have the first baseline and last baseline options. The assumption, here, is that this is partly because writings aren’t commonly written with words read in 90 degrees from top to bottom. Words are generally written and read on the horizontal axis forward or backward, commonly dependent on the language in use.

The remaining vertical axis alignments are fill and center.

Fill looks like this:

img

Center looks like this:

img

Those are the four vertical axis alignment properties. Next up: the distribution property.

Distribution property

You’ve seen that alignment controls the views’ layout perpendicular to the axis. Distribution controls the views’ layout parallel to the axis.

There are five distribution properties in both the horizontal and vertical axes. They are as follows:

  • Fill: Size the subviews to fill up all of the space along the axis, but one view may size differently than the others.
  • Fill Equally: Size the subviews to fill up all of the space along the axis, but make each subview have the same size.
  • Fill Proportionally: Size the subviews to fill up all of the space along the axis, but resize each subview proportional to its intrinsic size.
  • Equal Spacing: Size the subviews according to their intrinsic size or other constraints, but position them so that the space between each of them is equal and at least the stack view’s Spacing property.
  • Equal Centering: Size the subviews according to their intrinsic size or other constraints, but position them so that the distance between each of the centers is equal.

Which distribution do you think is fitting for having three buttons of equal size?

You’re correct if you picked the fill equally distribution. The fill equally distribution will help you achieve buttons of equal sizes within a stack view.

In Profile.storyboard, select the horizontal stack view.

Set its distribution to Fill Equally in the Attributes inspector, and set the spacing to 0.

Your horizontal stack view will now look like this:

img

Nesting stack views

Just when you think stack views couldn’t get more powerful — BAM! — nested stack views. Yes, you read that right. It’s possible to have a stack view of stack views.

Embed the vertical stack view and the horizontal stack view into a vertical stack view.

Set the spacing of the container stack view to 16. Set the alignment to Fill.

Next, add the following constraints to the stack view:

  • 24 points spacing between stack view’s top edge and Safe Area’s top edge.
  • 20 points spacing between the stack view’s leading edge and superview’s leading edge.
  • Align horizontally in container’s center.

At the moment, the label’s text attributes determine the image view’s width. But what if you don’t want the image view to expand or shrink depending on other views? What if you want the image view to have a fixed width without affecting the label’s ability to expand or shrink according to its text attributes?

You can achieve this type of arrangement without adding many additional constraints using spacer views.

Create a stack view with left spacer view, the profile image view and right spacer view arrangements using the following steps:

  1. Embed the Profile Image View into a stack view.
  2. Set the stack view’s axis to horizontal.
  3. Drag two UIView objects into the stack view.
  4. Position a UIView on the left, the profile image view in the middle, and a UIView on the right in the stack view.
  5. Add an equal width constraint between the left spacer view and the right spacer view.
  6. Set both the left and right spacer views’ background color to clear
  7. Change the profile image view’s width constraint priority to 1000.

In the document outline, your Profile View Controller scene will look something like this:

img

When not to use stack views

When you layout the UI, the first tool that comes to mind should be the stack view. A stack view reduces the number of constraints, makes adding and removing views trivial, supports intuitive animations and more.

That said, there are times when a stack view may not be your best option. For example, when your views need to behave differently than a stack view’s default behaviors. In that case, you may want to work with manual constraints instead.

Imagine you have a stack view with a left view and a right view. The stack view has a fixed height, and its subviews have equal width. When you animate the right view’s isHiddenproperty from false to true, the stack view animates your views a certain way. The left view will take up the space of the right view as the left view expands, and the right view shrinks. If the animation behavior is not what you want, then you may find manual constraints to be a more suitable solution for the UI you want to achieve.

In addition to the stack view’s behavior expectations, you may be working on a legacy codebase. The legacy codebase may have a pre-iOS 9.0 deployment target. This means that if you use stack views, you would need to support both pre-iOS 9.0 and iOS 9.0 and newer. This is a maintenance consideration that may affect crucial business decisions. This is another example of when stack views can be a less viable solution in comparison to creating additional constraints.

Overall, the stack view is a great tool that provides flexibility and adaptability to your user interface.

Challenges

The profile view controller could use a facelift. Add a background view with the following specifications:

  1. Embed the container stack view into a background view.
  2. Align the container stack view bottom edge equal to the background view’s bottom edge with 8 points spacing.
  3. Align the container stack view top edge equal to the background view’s superview’s top edge with 26 points spacing.
  4. Set the container stack view leading edge greater than or equal to the superview’s leading edge with 20 points spacing.
  5. Set the background view’s background color to Group Table View Background Color.
  6. Set the background view’s leading, top and trailing edges equal to the superview.
  7. Add a constraint that aligns the background view’s bottom edge greater than or equal to the superview’s Safe Area bottom edge with a zero constant.

img

Key points

  • The stack view is a smart container that positions and sizes the views contained within itself.
  • Stack views empower developers to create adaptive layouts using fewer Auto Layout constraints.
  • Use stack view properties to modify the positions and sizes of the views within itself.
  • You can nest stack views within another stack view.
  • When creating layouts, a stack view should be your go-to tool. However, there are occasions where you may not want to use stack views.