In iOS 9, Apple introduced a very handy new UI concept: the UIStackView. Stack views help us quickly compose sequential “stacks” of views without Auto Layout. UIStackView offers a number of distribution and spacing options in Interface Builder. If you’re unfamiliar with UIStackView, I recommend reading “Exploring UIStackView Distribution Types” first.

In this post, I’ll describe how to use the Fill Proportionally option with any custom view while enjoying fine-grained control over the proportions themselves.

The Problem with Proportional Distribution

UIStackView ensures that its arranged subviews maintain the same proportion to each another as your layout grows and shrinks. However, unlike the other distribution options, views that are proportional must have an intrinsic content size. The trouble with that is not all views have an intrinsic size, including UIView itself. Happily, there is a workaround that allows us to adjust proportions of arbitrary UIViews.

Project Setup and Equal Distribution

For this post, I created a new single-view project and added a vertical UIStackView to the view controller. Now let’s add a new view (.xib file) as well as a corresponding .swift implementation that subclasses UIView.

In the code here, we’ll call our new view CustomView. Be sure you set your view’s custom class in the .xib file. Our view will be empty to demonstrate proportional filling with absolutely no intrinsic content size.

Create a new view

Our custom view in IB

In the view controller, let’s create a function called “addSubviews” that loads three of our custom UIViews from the nib file and programmatically adds them to the vertical stack view. The subviews are added when the view controller is loaded. Finally, set the background color of each subview:

In Interface Builder (IB), set the stack view’s fill distribution to Fill Equally. This distribution style doesn’t need an intrinsic content size, so we can run the app and see that our views are laid out equally as we expect.

Custom views distributed equally

Fill Proportionally

Fill Equally works as expected. What if we’d like to define a proportional fill for our UIViews? For this app, I want to set the proportions between red:green:blue to 3:2:1, respectively. To do this, go into IB, change the UIStackView’s distribution setting to Fill Proportionally, and run the app. Here is the result:

The problem

Here is where developers run into trouble. UIViews alone do not have intrinsic content sizes. If you have a custom view containing UI elements (say, arranged using Auto Layout) that you want to distribute given an arbitrary proportion, it won’t work without some more code.

The trick to fixing this issue is to override intrinsicContentSize in our custom UIView. In fact, you don’t even need to provide exact numbers for the size because the UIStackView will manage it for us.

Instead, we can set the size to the desired proportions. We’ll add a single var to our class to allow the caller to adjust the proportions:

This approach also works for horizontal stack views. In that case, simply alter the width of the CGSize returned by intrinsicContentSize. If we want the height proportions of the views to be 3:2:1, we can set the heights accordingly:

Running the app, we can see the views have laid out exactly as we wanted.

Perfect!

On the whole, I have found UIStackViews to be an efficient and flexible way to compose user interfaces in iOS. This approach is great because it gives the developer more fine-grained control over how the stack view lays out proportionally, without the bother of Auto Layout.

I wanted a top permanent view and then display one of the two remaining views depending on what happens in the top view. What I see that I can do is hide the blue view and then animate the showing of the blue while hiding the green. And then reverse it. It works nicely. I thought I had to change their sizes (e.g., from 0 to 1 and back again) but using .isHidden works very well. Thanks for writing this article it was a tremendous help as I was getting all sorts of constraint errors trying to figure it out myself.

Glad you figured it out! Yes, we’ve had good luck with isHidden as well.

One issue to be aware of with UIStackViews that we’ve found on projects is– if you use a lot of them, especially in reusable UI elements like table view cells– the adding/removing of views can lead to poor performance (e.g., a less-than-smooth scrolling of your table view).

Thanks. What I’ve done is create 4 UIViewControllers in Interface Builder. In one (MainVC), there is a UIStackView pinned on all 4 sides. The three others (red, green, blue) are added to the stackView in MainVC.viewDidLoad():

each is loaded via storyboard.instantiateViewController() and then addChildViewController to this main view controller.

Note: the base UIView of each (red, green, blue) VC has its class set to CustomView so it will deliver an intrinsic size.

The redVC.view is set with a height and high content hugging priority. Then redVC.view is added to the stackView’s arranged subviews.

The blue and green VC.view are also set with a height, but their content hugging priority is set lower. The blueVC.isHidden = true while green is visible. blueVC.view and greenVC.view are also added to the stackView’s arranged subviews.

A button in the redVC causes the blueVC.view and greenVC.view isHidden values to switch within a UIView.annimate block.

Its very cool, and does exactly what I want: use IB to design my interfaces, keeps the complexity of the content of red, blue, green controllers separate (I use a delegate to communicate the actions in redVC to the mainVC), all the while making things look and behave great.

Yea, I sure know about this. I used It and found out that width constraint for last view in stackView is disabled, that knowledge, unfortunately, doesn’t help me understand this odd UIStackView behaviour.