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.

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:

Storyboard StackView Hierarchy

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

Never miss a post!

iOS Size Classes Cheat Sheet

Subscribe and get my free iOS Size Classes Cheat Sheet

Success! Now check your email to confirm your subscription and download your free guide to iOS Size Classes.

There was an error submitting your subscription. Please try again.

Unsubscribe at any time.
No time to watch WWDC videos?

Sign up to get my iOS posts direct to your inbox and I will send you a free PDF of my iOS Size Classes Cheat Sheet.

OK! Check your inbox (or spam folder) for an email to confirm your details and download your free guide to iOS Size Classes.

There was an error submitting your subscription. Please try again.

Unsubscribe at any time.
Archives Categories