Last updated: Jun 12, 2020
I had an interesting question this week about stack views and size classes that I thought was worth sharing. Suppose you have a stack view containing three views and you want to hide one of them for certain size classes. For example with the vertical stack shown below what if I want to hide the bottom heart view when the device has a compact vertical size class?
The writer was trying to do this using size classes in Interface Builder to control when to install the view. For example, to not have the view installed when the height is compact:
At first this looks like it will work. When you rotate the device to landscape the third view disappears as expected. Unfortunately when you rotate back to portrait the extra view shows up in the wrong spot:
The problem is that the stack view does not expect you to install and remove views in this way. It expects you to use its API (addArrangedSubview
and removeArrangedSubview
) so that it can automatically update constraints for the views it is managing.
A Better Approach - Hide the View
Luckily there is a much easier way. A stack view will automatically adjust the layout when you hide or unhide one of its views. So instead of using size classes in Interface Builder create an outlet for the view to our view controller.
@IBOutlet var extraHeart: UIImageView!
Then in the view controller we can use willTransitionToTraitCollection
to test for the compact height and hide or unhide the view.
override func willTransition(to newCollection: UITraitCollection,
with coordinator: UIViewControllerTransitionCoordinator) {
super.willTransition(to newCollection, with: coordinator)
configureView(newCollection.verticalSizeClass)
}
The configureView
method hides and unhides the view based on the vertical size class (the guard makes sure we do not try this before the system has set our outlet):
private func configureView(_ verticalSizeClass: UIUserInterfaceSizeClass) {
guard extraHeart != nil else {
return
}
extraHeart.hidden = (verticalSizeClass == .compact)
}
The traitCollection
property of the view controller gives us the size class to configure the view when we first load:
override func viewDidLoad() {
super.viewDidLoad()
configureView(traitCollection.verticalSizeClass)
}
That’s all you need to hide the view for compact heights:
The updated sample code is in my Code Examples GitHub repository.