Stack View Background Color in iOS 14

Stack views show their background color in iOS 14! I recommend checking your storyboards. If you have inadvertently set a background color you’re in for a surprise when they show up when you build against the iOS 14 SDK.

Non-rendering Views

When Apple introduced stack views in iOS 9 working with Auto Layout got easier. As great as stack views are, it was always frustrating that stack views ignored their background color. You could set the property in code or in a storyboard but it had no effect.

The explanation from the Apple documentation is that a stack view is never itself rendered - it’s purpose is to manage its arranged subviews:

The UIStackView is a nonrendering subclass of UIView; that is, it does not provide any user interface of its own. Instead, it just manages the position and size of its arranged views. As a result, some properties (like backgroundColor) have no effect on the stack view.

If you want to give your stack view a background color you can manually insert an unmanaged subview at the back of the stack view and pin it to the stack view edges.

What Changed in iOS 14?

In iOS 14, UIStackView has changed from using a CATransformLayer to using a CALayer. I saw this first mentioned by Renaud Lienhart (@layoutsubviews) and then confirmed by David Duncan (@rincewindsama). This change is not (yet) mentioned in any release note or documentation (bug report FB8363575).

You use a CATransformLayer to create 3D layer hierarchies and unlike a CALayer it doesn’t display any content of its own. This means it ignores the CALayer properties such as backgroundColor. For a further discussion of CATransformLayer and all things Core Animation I still find iOS Core Animation by Nick Lockwood useful despite its age.

You can see this if you inspect a stack view in the debugger. First running on iOS 13 (note the layer property):

(lldb) po rootStackView
<UIStackView: 0x7fdc0270b3c0; frame = (0 0; 0 0);
layer = <CATransformLayer: 0x600002de3e40>>

Then on iOS 14 where we now have a CALayer:

(lldb) po rootStackView
<UIStackView: 0x7fa253f0bdc0; frame = (0 0; 0 0);
layer = <CALayer: 0x600003339440>>

I don’t know what caused Apple engineers to choose CATransformLayer back in iOS 9 or switch to CALayer in iOS 14. I’m sure they have their reasons. What it means though is that a stack view will now render any CALayer properties you set, including backgroundColor.

If you create your stack view in a storyboard you can set the background color and Xcode 12 will now show it in the canvas:

Xcode 12 stack view with a purple background

It also works if you set it in code:

stackView.backgroundColor = .systemPurple

Either way the background color shows up when run on iOS 14 but not if run on iOS 13 or earlier:

Stack view with background visible only on iOS 14

Since we now have a full CALayer we can also set other properties like cornerRadius on the stack view layer. If you’re still building for iOS 13 or earlier it’s best to do that inside an availability test to avoid runtime warnings when you have a CATransformLayer.

changing property cornerRadius in transform-only layer, will have no effect

For example, we can directly set the background color and corner radius for iOS 14 and fallback to manually adding a background view:

if #available(iOS 14.0, *) {
  stackView.backgroundColor = .systemPurple
  stackView.layer.cornerRadius = 10.0
} else {
  // Fallback to manually adding
  // background view
}

Stack view with purple background and corner radius

What Should You Do?

My assumption is that Apple doesn’t expect this change to cause big problems. Most projects will not have set a background color on a stack view as it had no effect before iOS 14.

This is probably reasonable but I suggest checking your storyboards to make sure somebody has not inadvertently set a background color on a stack view that you do not want.

I guess I need to update my book

Read More