Stack View Background Color

How do you set a background color for a stack view? For that matter how do overlay foreground or other decorator views over a stack view? If you have used UIStackView you probably know that it is not a normal subclass of UIView. Setting its backgroundColor has no effect. What we can do is add subviews that are not managed by the stack view.

Last updated: Nov 10, 2022

Starting with iOS 14, you can set the backgroundColor directly for a UIStackView. See Stack View Background Color in iOS 14 for details.

Non-rendering View

From the header file for UIStackView:

UIStackView is a non-rendering subclass of UIView, intended for managing layout of its subviews.

So the reason backgroundColor has no effect for a stack view is that it is never drawn (rendered).

Using Un-arranged Subviews

A stack view manages the layout of views added to its arrangedSubviews array. It is easy to forget that like every UIView it also has a subviews array. In fact the stack view makes sure that arrangedSubviews is a subset of subviews. When you use addArrangedSubview the stack view adds the view to arrangedSubviews but also checks and adds the view to subviews if it is not already there.

It is perfectly ok for us to add extra subviews to a stack view that are not part of arrangedSubviews. The stack view will not add constraints for these views - we can do that ourselves. Perfect for things like background or overlay views.

The order of the subviews array sets the order from background to foreground of the displayed views (regardless of whether the views are also in arrangedSubviews).

The Setup

Here is my setup so we can try this out. I embedded two images in a horizontal stack view which is then embedded in a vertical stack view containing a switch:

Stack view scene in storyboard

I will skip the details but I have pinned the root stack view to the top, leading and trailing margins of the super view. I also connected outlets in the view controller:

class RevealViewController: UIViewController {
  @IBOutlet private weak var rootStackView: UIStackView!
  @IBOutlet private weak var imageStackView: UIStackView!
  @IBOutlet private weak var revealSwitch: UISwitch!

Adding a Background View

As a first step we can add a background to the root stack view. Let’s create a purple background view with rounded corners:

private lazy var backgroundView: UIView = {
  let view = UIView()
  view.backgroundColor = .purple
  view.layer.cornerRadius = 10.0
  return view
}()

To make it appear as the background we add it to the subviews array of the root stack view at index 0. That puts it behind the arranged views of the stack view. We also need constraints to pin it to the edges of the stack view. I have a short method to do that:

private func pinBackground(_ view: UIView, to stackView: UIStackView) {
  view.translatesAutoresizingMaskIntoConstraints = false
  stackView.insertSubview(view, at: 0)
  view.pin(to: stackView)
}

Don’t forget to disable translatesAutoresizingMaskIntoConstraints for the background view. I am using a small extension on UIView to pin a view to another view:

public extension UIView {
  public func pin(to view: UIView) {
    NSLayoutConstraint.activate([
      leadingAnchor.constraint(equalTo: view.leadingAnchor),
      trailingAnchor.constraint(equalTo: view.trailingAnchor),
      topAnchor.constraint(equalTo: view.topAnchor),
      bottomAnchor.constraint(equalTo: view.bottomAnchor)
      ])
  }
}

I call the pinBackground from viewDidLoad:

override func viewDidLoad() {
  super.viewDidLoad()
  pinBackground(backgroundView, to: rootStackView)
}

Here is what our root stack view looks like with a rounded purple background:

Purple background

Adding an Overlay or Foreground View

We don’t need to stop with a background view. We can add views at any position in subviews. For a foreground view use addSubview to add the view at the end of the list:

private func pinForeground(_ view: UIView, to stackView: UIStackView) {
  view.translatesAutoresizingMaskIntoConstraints = false
  stackView.addSubview(view)
  view.pin(to: stackView)
}

Let’s have a yellow cover view:

private lazy var coverView: UIView = {
  let view = UIView()
  view.backgroundColor = .yellow
  return view
}()

and pin it to the image stack view:

override func viewDidLoad() {
  super.viewDidLoad()
  pinBackground(backgroundView, to: rootStackView)
  pinForeground(coverView, to: imageStackView)
}

The yellow view is in the foreground of the image stack view so the two image views are no longer visible:

Cover view

Let’s hook up the switch action to toggle the alpha property of the cover view to reveal the two image views that are underneath. For extra fun we can animate the change:

@IBAction func revealAction(_ sender: UISwitch) {
  UIView.animate(withDuration: 0.25) {
    self.configureReveal()
  }
}
 
private func configureReveal() {
  coverView.alpha = revealSwitch.isOn ? 0 : 1.0
}

The final version of viewDidLoad to also configure the cover view:

override func viewDidLoad() {
  super.viewDidLoad()
  pinBackground(backgroundView, to: rootStackView)
  pinForeground(coverView, to: imageStackView)
  configureReveal()
}

The final effect is not going to win any design prizes but I hope you get idea:

Get the Code

  • RevealStack - Xcode project containing the full code from this post

Learn More

I dig deeper into stack views and how to use them to build adaptive layouts in my book: