Like every iteration of iOS, iOS 9 brings forth a lot of new features. UIKit changes with every release of iOS, but one particular addition in iOS 9 will change how developers fundamentally think about creating user interfaces on iOS, UIStackView
. In this tutorial, you will learn how to use UIStackView
to create user interfaces.
This article assumes you are familiar with the basics of Auto Layout. If this subject is new to you, then our introductory tutorial about Auto Layout is a good place to start. To understand why stack views are so helpful and how they operate, a solid understanding of Auto Layout is required.
1. Demo Preview
Using UIStackView
, we are going to build a mock screen that prompts the user to leave a rating for their app. The user can add or remove stars in the user interface to indicate their rating. It will look like this when we are finished.
Download the starter project from GitHub and open it. You'll see that there are two stack views inside Main.Storyboard.
We will use the two stack views to lay out the user interface. Before we begin coding, let's first take brief look at how a stack view works.
2.UIStackView
Overview
At its core, the stack view is an interface for laying out several subviews, either vertically or horizontally. If you have Android development experience, it's similar to the LinearLayout
control.
One of the main advantages of the stack view is that it will automatically create Auto Layout constraints for each subview that's added to it. You also have finite control over how those subviews are sized and positioned. There are options to configure the sizing of the views, where they should be arranged, as well as how much padding should be between the subviews.
Laying Out Content
To see the options of a stack view, open Main.Storyboard and select one of of the stack views. In the Attributes Inspector, notice the options listed under Stack View.
The Axis determines if the stack view arranges its subviews horizontally or vertically. The Alignment controls how the actual subviews should be aligned. Distribution defines how the subviews are sized and Spacing controls the minimum spacing between the subviews of the stack view.
To simplify these terms, think of it like this. Alignment controls the X and Y values while Distribution controls height and width. The other two values can also affect alignment. Baseline Relative, if checked, will derive vertical spacing of each subview from its baseline. Layout Margins Relative places the subviews relative to the standard layout margins if selected.
Another important thing to remember when working with a stack view is that it's built to be a container view. Because of that, it's a nonrendering subclass of UIView
. It's not draw to the canvas like other UIView
subclasses. This means that setting properties like backgroundColor
or overriding the drawRect:
method will have no effect on the stack view.
subviews
and arrangedSubviews
Before we start working with stack views, I'd like to focus on the difference between a stack view's subViews
and arrangedSubviews
properties. If you wish to add a subview for the stack view to manage, you do so by calling addArrangedSubview
: or insertArrangedSubview:atIndex:
. The arrangedSubViews
array is a subset of its subViews
property.
To remove a subview that the stack view manages, you need to call both removeArrangedSubview:
and removeFromSuperview
. Removing an arranged subview ensures the stack view will no longer manage that view's constraints. It doesn't take it out of the view hierarchy. This is very important to remember.
Now that we have a solid understanding of how the stack view works, let's start using a stack view.
3. Configuring the Vertical Stack View
Open Main.Storyboard and select the top stack view. In the Attributes Inspector, make the following changes:
- set Alignment to Center
- set Distribution to Equal Spacing
- set Spacing to 30
This will tell the stack view to try and add constraints that center all of the subviews vertically and size them so that they fill the stack view's axis evenly. In addition, it will add 30 points of padding to the subviews.
If the subviews are not able to fit inside the stack view, it will shrink them based on their respective compression resistance priorities. This could happen if you were to add subviews to the stack view at run time as we'll see later.
If there is any ambiguity, the stack view will fall back to shrinking the subviews based on their index in its arrangedSubviews
array until they fit the stack view's bounds.
4. Adding Vertical Stack Subviews
Add a label, an image view, and a button to the top stack view by dragging them in the document outline. Ensure the label is at the top, the image view in the middle, and the button at the bottom. The storyboard's document outline should look like this after you've added these subviews:
Next, we'll need to edit some of the properties of the subviews we just added, using the Attributes Inspector. For the label, change its Text to "How do you like our app?" and select Center for Text Alignment. As for the image view, enter "logo" for the Image and Aspect Fit for the Content Mode. For the button, set its Text to "Add Star!".
Go ahead and run the app. You'll see that with very little work, you have added three subviews that respond to any changes in orientation, size class, etc. In fact, you didn't even have to manually add any constraints.
While the app is running, click the Debug View Hierarchy button at the bottom of the Xcode window to initiate live view debugging.
Select any of the three subviews you added earlier. Look at the size inspector and notice the constraints that were added by the stack view. The picture below is showing the constraints added for the button.
The button to add stars for our mock app isn't hooked up yet. Let's fix that now. Stop the app and open the storyboard. Create an IBAction with a name of addStar for the Touch Up Inside event.
Add the following code inside the addStar(_:)
method:
@IBAction func addStar(sender: AnyObject) { let starImgVw:UIImageView = UIImageView(image: UIImage(named: "star")) self.horizontalStackView.addArrangedSubview(starImgVw) UIView.animateWithDuration(0.25, animations: { self.horizontalStackView.layoutIfNeeded() }) }
We add a star image to the horizontal stack view with an animation. Remember, since stack views manage Auto Layout constraints for us, we only need to call layoutIfNeeded
to create an animation.
Build and run the app again and add a star. You'll see that the end result is not what we were hoping for.
If you look at the Attributes Inspector with the bottom stack view selected, the problem should be clear. Since both the Alignment and Distribution are set to Fill, the stack view is stretching the star to fill its bounds.
This will also cause more problems when more stars are added. We want the stars to be centered, not stretched to fit the width of the stack view. Change Alignment to Center and Distribution to Fill Equally. Finally, update the addStar(_:)
method by setting the image view's Content Mode to Aspect Fit.
@IBAction func addStar(sender: AnyObject) { let starImgVw:UIImageView = UIImageView(image: UIImage(named: "star")) starImgVw.contentMode = .ScaleAspectFit self.horizontalStackView.addArrangedSubview(starImgVw) UIView.animateWithDuration(0.25, animations: { self.horizontalStackView.layoutIfNeeded() }) }
Build and run the app one more time. Add a few stars and notice how the stack view correctly centers them along its axis.
6. Nesting Stack Views
Our app wouldn't be very helpful without the ability to remove stars. Open the storyboard and add a horizontal stack view to the top stack view's view hierarchy. Make sure it's positioned below the image view for the logo and above the button.
Drag the "Add Star!" button inside the newly added stack view and add second button to the new stack view. Change the button's title to "Remove Star" and select red for the text color. The document outline should now look like this:
Edit the attributes of the new stack view in the Attributes Inspector, making the following changes:
- set Alignment to Center
- set Distribution to Equal Spacing
- set Spacing to 10
7. Removing Stars
Create an IBAction for the "Remove Star" button with a name of removeStar for the Touch Up Inside event.
Add the following code to the removeAction(_:)
method:
@IBAction func removeStar(sender: AnyObject) { let star:UIView? = self.horizontalStackView.arrangedSubviews.last if let aStar = star { self.horizontalStackView.removeArrangedSubview(aStar) aStar.removeFromSuperview() UIView.animateWithDuration(0.25, animations: { self.horizontalStackView.layoutIfNeeded() }) } }
Build and run the app. You should now be able to both add and remove stars. Change the orientation of the iOS Simulator or rotate your testing device to see how the app behaves by adjusting its user interface. Remember that we've built the the user interface of this app without having to manually add a single constraint.
Keep in mind that the removeFromSuperview
call in the removeStar(_:)
method is essential to remove the subview from the view hierarchy. Recall that removeArrangedSubview(_:)
only tells the stack view that it no longer needs to manage the subview's constraints. The subview, however, remains in the view hierarchy until we remove it from its superview by calling removeFromSuperview
on it.
Conclusion
The UIStackView
class greatly simplifies developing user interfaces. This is a good thing, especially when the hardware the application runs on can vary so much. With UIStackView
, developers can spend less time setting up tedious constraints for simple scenarios, shifting the heavy lifting to UIKit.
If you got stuck at any point during this tutorial, feel free to download the finished project from GitHub.