A Brief iOS9 UIStackView Guide

Posted on September 8, 2015 中文版

This Swift 2.0 Guide requires Xcode7 beta or later, get the complete project from GitHub or zipped.

iOS9 introduces UIStackViews, a new component that greatly simplifies building layouts that can be broken down to vertical or horizontal sequences of UIViews, providing an alternative to manually positioning views using auto-layout.

Acting as an invisible container, each UIStackView is able to display a single sequence of subviews (arranged views) aligned either vertically or horizontally, automatically resizing its content according to the current screen size and adapting to changes in orientation. How these subviews are actually positioned depends on a few properties that define how the subviews should be aligned, spaced and, if needed, resized.

What happens under the hood is that the UIStackView class manages auto-layout constraints for you. Think of UIStackView as an abstraction layer above auto-layout that simplifies the creation of a well defined subset of layouts. You can start building your layout from a main UIStackView and add nested UIStackView until all your UIViews are positioned correctly.

If you have done any Android development, you’ll notice that the UIStackView concept is quite similar to LinearLayouts, likely the most used android layout scheme, that in turn borrowed some ideas and improved upon the multitude of layouts that were already available in Java Swing.

The Basics

As usual, UIStackViews can be created both programmatically and in Interface Builder.

In Interface Builder you can add a new vertically or horizontally aligned UIStackView choosing the right control from the Object Library and once the view is in place, new views can be added dragging controls inside the UIStackView.

A new UIStackView can also be wrapped around one of more existing views, just select them and click the new Stack icon you’ll find in the bottom bar of Interface Builder.

Quite simple, but in this guide we’ll create a basic nested layout programmatically.

UIStackView example

In this brief example, a vertical UIStackView, placed right below the status bar, will contain four controls: two UILabels, one horizontal UIStackView and one UIButton. Three buttons with default icons will be placed inside an inner horizontal UIStackView.

Let’s start, create a new Single View Application, verifying that the selected Deployment Target is 9.0+.

Open your only ViewController and replace the viewDidLoad method with this one:



    var stackView:UIStackView!
    var nestedStackView=UIStackView()

    override func viewDidLoad() {
        super.viewDidLoad()

		stackView.translatesAutoresizingMaskIntoConstraints=false
        self.view.addSubview(stackView)
		// Main UIStackView contraints, nearly fills its parent view
        self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-30-[stackView]-30-|",
            options: NSLayoutFormatOptions.AlignAllLeading,metrics: nil, views: ["stackView":stackView]))
        self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-10-[stackView]-10-|",
            options: NSLayoutFormatOptions.AlignAllLeading,metrics: nil, views: ["stackView":stackView]))

        stackView.axis = .Vertical
        stackView.alignment = .Fill
		stackView.spacing = 25
        stackView.distribution = .FillEqually
        
        var lbl = UILabel()
        lbl.text="Label 1"
        lbl.backgroundColor = UIColor.redColor()
        stackView.addArrangedSubview(lbl)
        
        lbl = UILabel()
        lbl.text="Label 2"
        lbl.backgroundColor = UIColor.greenColor()
        stackView.addArrangedSubview(lbl)

        nestedStackView.axis = .Horizontal
        nestedStackView.alignment = .Fill
        nestedStackView.spacing = 25
        nestedStackView.distribution = .FillEqually
        nestedStackView.addArrangedSubview(UIButton(type: .InfoDark))
        nestedStackView.addArrangedSubview(UIButton(type: .InfoLight))
        nestedStackView.addArrangedSubview(UIButton(type: .ContactAdd))
        stackView.addArrangedSubview(nestedStackView)
        
        let btn=UIButton(type: .System)
        btn.setTitle("Press Me", forState: .Normal)
        stackView.addArrangedSubview(btn)
        
    }

To specify a vertical orientation for the main UISTackView we are setting the axis property to .Vertical, the first three controls will be equally spaced and the UIButton will fill the rest of the available space. The three buttons with default styles contained in the inner nestedStackView will be arranged in a similar fashion. The alignment, distribution and spacing properties will be explained in the next section, ignore them for now.

Sooner or later you will need to hide and show some of the arranged views and this is quite straightforward for UIStackViews, just set the hidden property of one of your views.

To test this, let’s add an action to the UIButton and a new pressedMe method as follow:



		...
		btn.setTitle("Press Me", forState: .Normal)
        btn.addTarget(self, action: "pressedMe:", forControlEvents: UIControlEvents.TouchUpInside)
        stackView.addArrangedSubview(btn)
        
    }
    
    func pressedMe(sender: UIButton!){
        UIView.animateWithDuration(0.5) {
            self.nestedStackView.hidden = !self.nestedStackView.hidden
        }
    }

Clicking the button will now hide or show with a short animation the inner UIStackView and the main UIStackView will reposition the remaining views according to the properties specified in viewDidLoad.

If needed, subviews can also be completely removed from the UIStackView and all the contained arranged views will be, again, repositioned according to the current properties.


    
    func pressedMe(sender: UIButton!){
        stackView.removeArrangedSubview(nestedStackView)
        nestedStackView.removeFromSuperview()
    }

Removal is a two step process, calling the removeArrangedSubview method will remove the view from the UIStackView and reposition the remaining subviews but will not remove the view from its superview. The removed views need to be completely removed from their super views too, or they will still be shown outside the UIStackView. To do this, simply invoke removeFromSuperview on the removed view.

UIStackView: Alignment, Distribution And Spacing

Let’s take a look at the positioning properties UIStackView exposes:

UIStackView properties

Axis

Defines along which axis your views will be positioned, has two possible values: Vertical, Horizontal.

Alignment

The alignment property specifies the perpendicular (to the selected axis) alignment for your views, the value Fill will also resize all your views to fill the available space, the other values will not modify your views size. Available values are: Fill, Leading, Top, FirstBaseline, Center, Trailing, Bottom, LastBaseline.

Distribution

Distribution specifies how the subviews should be resized or distributed to fill all the available space along the axis, the possible values can be divided in two groups: fill and spacing values.

Fill values modify the size of the subviews if they don’t fill (or fit in) all the available space. The spacing between the subviews will be the one specified with the spacing property.

  • Fill: The subviews will be shrinked or stretched according to their content content resistance or hugging priority. If you didn’t set any, one of the subviews will be modified to fill the space available.
  • FillEqually: Disregarding any constraint, the subviews will be resized to the same size along the axis.
  • FillProportionally: The subviews will be proportionally resized according to the original size of each subview.

Spacing values fill the space along the axis altering the spacing between the subviews, the size of the subviews will be modified, according to the compression resistance, only if the subviews still don’t fit or if any auto-layout ambiguity arises.

  • EqualSpacing: The subviews will be equally spaced
  • EqualCentering: The subviews center axis will be equally spaced

Spacing

The spacing property is expressed in points and its meaning depends on the current distribution value.

If the UIStackView distribution property is either EqualSpacing or EqualCentering, the spacing property will represent the minimum spacing among subviews. Alternatively, if a FillProportionally distribution was selected, the requested spacing value will be exactly the chosen value.

UIStackViews: iOS7+ Backports

UIStackViews are supported only from iOS9 onward but a few backports, that partially implements this feature in iOS7 or later, have already been built:

Did you like this article? Let me know on Twitter!

I'm also on Twitter and GitHub.

Subscribe via RSS or email.