I recently wrestled with auto layout errors when hiding a UIStackView
. Rather than do a bunch of book keeping and wrapping stacks in UIViews
, I opted to create an outlet for my parentStackView
and outlets for the children I want to hide/unhide.
@IBOutlet weak var parentStackView: UIStackView!
@IBOutlet var stackViewNumber1: UIStackView!
@IBOutlet var stackViewNumber2: UIStackView!
In storyboard, here's what my parentStack looks like:
It has 4 children and each of the children have a bunch of stack views inside of them. When you hide a stack view, if it's got UI elements that are stack views as well, you'll see a stream of auto layout errors. Rather than hide, I opted to remove them.
In my example, parentStackViews
contains an array of the 4 elements: Top Stack View, StackViewNumber1, Stack View Number 2, and Stop Button. Their indices in arrangedSubviews
are 0, 1, 2, and 3, respectively. When I want to hide one, I simply remove it from parentStackView's
arrangedSubviews
array. Since it's not weak, it lingers in memory and you can just put it back at your desired index later. I'm not reinitializing it, so it just hangs out until it's needed, but doesn't bloat memory.
So basically, you can...
1) Drag IBOutlets for your parent stack and the children you want to hide/unhide to the storyboard.
2) When you want to hide them, remove the stack you want to hide from parentStackView's
arrangedSubviews
array.
3) Call self.view.layoutIfNeeded()
with UIView.animateWithDuration
.
Note the last two stackViews are not weak
. You need to keep them around for when you unhide them.
Let's say I want to hide stackViewNumber2:
parentStackView.removeArrangedSubview(stackViewNumber2)
stackViewNumber2.removeFromSuperview()
Then animate it:
UIView.animate(withDuration: 0.25,
delay: 0,
usingSpringWithDamping: 2.0,
initialSpringVelocity: 10.0,
options: [.curveEaseOut],
animations: {
self.view.layoutIfNeeded()
},
completion: nil)
If you want to "unhide" a stackViewNumber2
later, you can just insert it in the desired parentStackView
arrangedSubViews
index and animate the update.
parentStackView.removeArrangedSubview(stackViewNumber1)
stackViewNumber1.removeFromSuperview()
parentStackView.insertArrangedSubview(stackViewNumber2, at: 1)
// Then animate it
UIView.animate(withDuration: 0.25,
delay: 0,
usingSpringWithDamping: 2.0,
initialSpringVelocity: 10.0,
options: [.curveEaseOut],
animations: {
self.view.layoutIfNeeded()
},
completion: nil)
I found that to be a lot easier than doing bookkeeping on constraints, fiddling with priorities, etc.
If you have something you want hidden by default, you could just lay it out on storyboard and remove it in viewDidLoad
and update without the animation using view.layoutIfNeeded()
.