Swift 4 solution
Three simple steps:
Change the constraints, e.g.:
heightAnchor.constant = 50
Tell the containing
view
that its layout is dirty and that the autolayout should recalculate the layout:self.view.setNeedsLayout()
In animation block tell the layout to recalculate the layout, which is equivalent of setting the frames directly (in this case the autolayout will set the frames):
UIView.animate(withDuration: 0.5) { self.view.layoutIfNeeded()}
Complete simplest example:
heightAnchor.constant = 50self.view.setNeedsLayout()UIView.animate(withDuration: 0.5) { self.view.layoutIfNeeded()}
Sidenote
There is an optional 0th step - before changing the constraints you might want to call self.view.layoutIfNeeded()
to make sure that the starting point for the animation is from the state with old constraints applied (in case there were some other constraints changes that should not be included in animation):
otherConstraint.constant = 30// this will make sure that otherConstraint won't be animated but will take effect immediatelyself.view.layoutIfNeeded()heightAnchor.constant = 50self.view.setNeedsLayout()UIView.animate(withDuration: 0.5) { self.view.layoutIfNeeded()}
Since with iOS 10 we got a new animating mechanism - UIViewPropertyAnimator
, we should know that basically the same mechanism applies to it. The steps are basically the same:
heightAnchor.constant = 50self.view.setNeedsLayout()let animator = UIViewPropertyAnimator(duration: 0.5, timingParameters: UICubicTimingParameters(animationCurve: .linear))animator.addAnimations { self.view.layoutIfNeeded()}animator.startAnimation()
Since animator
is an encapsulation of the animation, we can keep reference to it and call it later. However, since in the animation block we just tell the autolayout to recalculate the frames, we have to change the constraints before calling startAnimation
. Therefore something like this is possible:
// prepare the animator first and keep a reference to itlet animator = UIViewPropertyAnimator(duration: 0.5, timingParameters: UICubicTimingParameters(animationCurve: .linear))animator.addAnimations { self.view.layoutIfNeeded()}// at some other point in time we change the constraints and call the animatorheightAnchor.constant = 50self.view.setNeedsLayout()animator.startAnimation()
The order of changing constraints and starting an animator is important - if we just change the constraints and leave our animator for some later point, the next redraw cycle can invoke autolayout recalculation and the change will not be animated.
Also, remember that a single animator is non-reusable - once you run it, you cannot "rerun" it. So I guess there is not really a good reason to keep the animator around, unless we use it for controlling an interactive animation.