Stack View Changes In iOS 15

Apple didn’t add any new features to stack views in iOS 15 but they have made a subtle change to the implementation that might catch you out when deploying back to earlier iOS versions.

Stack View Fill Distribution

One of the things that has always annoyed me about stack views is the undocumented and unexpected behaviour. Take a look at this example. I have a multi-line label and a switch in a horizontal stack view.

Multi-line label and switch in horizontal stack view

The stack view uses a fill distribution and center alignment. With a fill distribution the stack view stretches or squeezes views to fill the available width. Auto Layout decides which view to squeeze first by comparing content compression resistance priorities. The view with the lowest priority gets squeezed.

Since both the label and the switch have a default horizontal compression resistance priority of 750 this layout should be ambiguous. Using Xcode 13, Interface Builder does indeed report a problem with missing constraints:

Missing constraints error

Interface Builder is misleading here. The problem is not due to missing constraints. I want to squeeze the label before the switch so I need to lower the compression resistance priority of the label to 749:

Horizontal content compression resistance priority 749

That’s great and works as I would expect so what’s the problem? Here’s how it looks if I run the same layout on iOS 14 (or earlier) compared to iOS 15:

Stack view on iOS 14 and iOS 15

Running on iOS 14 the label and the switch are both sized to half the available width. Lowering the priority to 749 has not had the expected effect.

Disambiguation Constraints

Here’s how the constraints look in the view debugger when running on iOS 14.5. The UISV-text-width-disambiguation constraint is causing the unexpected behaviour. It has a priority of 759 and wants to make the label width half the width of the stack view:

UISV-text-width-disambiguation constraint at priority 759

The switch has hugging and compression priorities of 750 so the disambiguation constraint takes priority. Here’s how the constraints look for the same layout on iOS 15. The disambiguation constraint is gone:

iOS 15 stack view constraints in view debugger

I’ve covered this problem with stack views before. One solution is to increase the content hugging priority of the switch to 760 or higher. It then takes priority over the disambiguation constraint and keeps the switch from being stretched on iOS 14. That gives us a consistent layout on both iOS 15 and earlier releases but you shouldn’t have to use the view debugger to figure out how to use a stack view.

Why Change This Now?

I’d long since given up on ever getting a response from Apple to my bug report (FB5722350). I don’t know why they originally implemented the disambiguation constraint or why they changed it in iOS 15? There is no mention in any release note that I can find. My thanks to Renaud Pradenc for tipping me off.

The original disambiguation constraint was confusing but changing it now could cause a nasty surprise to anybody creating new stack view layouts in Xcode 13 when they deploy back to iOS 14 and earlier.

What Should You Do?

If you’re using Xcode 13 to create a new stack view layout I suggest you test the resulting layout on both iOS 15 and iOS 14 (or your minimum deployment target). If you want to learn more about stack views you might like my book Modern Auto Layout.