James P James P - 6 months ago 41
iOS Question

How do I get a UIStackView to update its layout?

I have a

UIStackView
with 5 subviews. I want to be able to animate the width of these subviews by updating their
intrinsicContentSize
or updating their constraints. How can I make the stackView redraw after updating the subview's layout?

UIView.animateWithDuration(0.3, animations: {
viewToResize.compressed = true //changes intrinsicContentSize
viewToResize.invalidateIntrinsicContentSize()
self.stackView.layoutIfNeeded() //makes no difference
anotherSubview.hidden = true //adding this makes everything work
})


I have tried various ways of trying to get the stackView to update, but nothing happens. The only way I can make it work is if I show/hide another subview inside the animation block, then the new layout is animated correctly, but I don't want to do this.

I have seen this question
Animating UIStackView arrangedSubview content size change
but the suggested answer does not work (nothing animates).

Answer

You don't need viewToResize.invalidateIntrinsicContentSize(), update constraint of your subviews instead. In the code below, we have two subviews in the stackView, and we create a widthConstraint outlet from storyboard. Then we can animate the width of the subview by updating the constant of widthConstraint in animation block. Note: If a view is contained in a stack view and you try to modify the view frame, it should not change

class ViewController: UIViewController {
    @IBOutlet var widthConstraint: NSLayoutConstraint!

    @IBOutlet var stackView: UIStackView!
    var compressed: Bool = true

    @IBOutlet var start: UIButton!

    @IBAction func onStart(sender: UIButton) {
        UIView.animateWithDuration(3.0,
                                   delay: 0.0,
                                   usingSpringWithDamping: 0.3,
                                   initialSpringVelocity: 10.0,
                                   options: .CurveLinear,
                                   animations: { () -> Void in
                                    //self.stackView.invalidateIntrinsicContentSize()
                                    self.widthConstraint.constant = (self.compressed == false) ? 100.0 : 200.0
                                    self.compressed = !self.compressed
                                    self.view.layoutIfNeeded()
            }, completion: nil)
    }

For more details, you can have a look at this great blog UIStackView, Auto Layout and Core Animation