Stack Views And Multi-Line Labels

Stack views can save you a lot of time when creating constraints. Unfortunately they are not without problems especially when using multi-line labels. Here is one situation where I think you should skip stack views and create your own constraints.

Take a look at this layout with a text label and a switch. In portrait on an iPhone the text wraps over several lines:

In landscape the label stretches to fill the available space. The switch keeps its natural size:

This looks like a job for a horizontal stack view. Adding constraints to pin the leading, top and trailing edges of the stack view leaves its height to grow with the content:

The stack view is using a .fill distribution and .top alignment. The label has its number of lines set to zero.

Setting Priorities

We need to help the stack view a little and adjust the content hugging priorities of the label. I want the switch to stay at its intrinsic content size and the label to resize to fill the available width. That means making the priorities for the label lower than for the switch.

The default content-hugging priority of the label (251) is already lower than the switch (750) so the label will stretch first. I need to lower the compression-resistance priority of the label (to 749) to make it lower than the default for the switch (750) so it also squeezes first. That should be enough. Unfortunately this is what happens:

It looks like the stack view has resized both the label and the switch making them half the width of the stack view. Not good.

Peeking Inside The Box

One of the problems with using a stack view is that we don’t control what constraints it adds for us. It is convenient when it works but frustrating when it does something unexpected. Luckily the view debugger lets us peek inside and see what it is doing:

The infamous (and undocumented) UISV-text-width-disambiguation constraint is the interesting one:

label.width = 0.5 * stackView.width - 0 @ 759

This is an optional constraint with a priority of 759 that wants to make the width of the label half the width of the stack view. Our switch has a content hugging priority of 750 which means this disambiguation constraint takes priority forcing both the label and switch to resize. Not what I wanted or expected.

There is an easy workaround. We can increase the content hugging priority of the switch to 760. That fixes the problem but it is not a solution that feels good to me. You should not need to use the view debugger to understand how to configure your layout priorities.

Knowing When To Quit

A stack view is a convenience that saves us from creating constraints for some common layouts. When you end up fighting with the stack view it is time to use a simpler approach and create the layout yourself.

I have an open bug report with Apple but in the meantime this is one situation where I avoid using a stack view and create the constraints myself:

See Also

A sample Xcode project reproducing the problem and a copy of the bug report filed on Open Radar: