How do I animate constraint changes?

animate constraints swift 4
swift change height constraint with animation
change constraint value with animation swift
swift animate change
add constraint with animation swift
constraint set animation swift
nsview animate constraints
increase view height with animation swift

I'm updating an old app with an AdBannerView and when there is no ad, it slides off screen. When there is an ad it slides on screen. Basic stuff.

Old style, I set the frame in an animation block. New style, I have a IBOutlet to the auto-layout constraint which determines the Y position, in this case it's distance from the bottom of the superview, and modify the constant:

- (void)moveBannerOffScreen {
    [UIView animateWithDuration:5 animations:^{
        _addBannerDistanceFromBottomConstraint.constant = -32;
    }];
    bannerIsVisible = FALSE;
}

- (void)moveBannerOnScreen {
    [UIView animateWithDuration:5 animations:^{
        _addBannerDistanceFromBottomConstraint.constant = 0;
    }];
    bannerIsVisible = TRUE;
}

And the banner moves, exactly as expected, but no animation.

UPDATE: I re-watched WWDC 12 talk Best Practices for Mastering Auto Layout which covers animation. It discusses how to update constraints using CoreAnimation:

I've tried with the following code, but get the exact same results:

- (void)moveBannerOffScreen {
    _addBannerDistanceFromBottomConstraint.constant = -32;
    [UIView animateWithDuration:2 animations:^{
        [self.view setNeedsLayout];
    }];
    bannerIsVisible = FALSE;
}

- (void)moveBannerOnScreen {
    _addBannerDistanceFromBottomConstraint.constant = 0;
    [UIView animateWithDuration:2 animations:^{
        [self.view setNeedsLayout];
    }];
    bannerIsVisible = TRUE;
}

On a side note, I have checked numerous times and this is being executed on the main thread.

Two important notes:

  1. You need to call layoutIfNeeded within the animation block. Apple actually recommends you call it once before the animation block to ensure that all pending layout operations have been completed

  2. You need to call it specifically on the parent view (e.g. self.view), not the child view that has the constraints attached to it. Doing so will update all constrained views, including animating other views that might be constrained to the view that you changed the constraint of (e.g. View B is attached to the bottom of View A and you just changed View A's top offset and you want View B to animate with it)

Try this:

Objective-C

- (void)moveBannerOffScreen {
    [self.view layoutIfNeeded];

    [UIView animateWithDuration:5
        animations:^{
            self._addBannerDistanceFromBottomConstraint.constant = -32;
            [self.view layoutIfNeeded]; // Called on parent view
        }];
    bannerIsVisible = FALSE;
}

- (void)moveBannerOnScreen { 
    [self.view layoutIfNeeded];

    [UIView animateWithDuration:5
        animations:^{
            self._addBannerDistanceFromBottomConstraint.constant = 0;
            [self.view layoutIfNeeded]; // Called on parent view
        }];
    bannerIsVisible = TRUE;
}

Swift 3

UIView.animate(withDuration: 5) {
    self._addBannerDistanceFromBottomConstraint.constant = 0
    self.view.layoutIfNeeded()
}

Animating Autolayout Constraints, So if you call layoutIfNeeded() inside the animations closure, it will animate any layout changes. layoutIfNeeded() will update every center and  How do I animate constraint changes? (9) I'm updating an old app with an AdBannerView and when there is no ad, it slides off screen. When there is an ad it slides on screen. Basic stuff. Old style, I set the frame in an animation block.

I appreciate the answer provided, but I think it would be nice to take it a bit further.

The basic block animation from the documentation
[containerView layoutIfNeeded]; // Ensures that all pending layout operations have been completed
[UIView animateWithDuration:1.0 animations:^{
     // Make all constraint changes here
     [containerView layoutIfNeeded]; // Forces the layout of the subtree animation block and then captures all of the frame changes
}];

but really this is a very simplistic scenario. What if I want to animate subview constraints via the updateConstraints method?

An animation block that calls the subviews updateConstraints method
[self.view layoutIfNeeded];
[self.subView setNeedsUpdateConstraints];
[self.subView updateConstraintsIfNeeded];
[UIView animateWithDuration:1.0f delay:0.0f options:UIViewAnimationOptionLayoutSubviews animations:^{
    [self.view layoutIfNeeded];
} completion:nil];

The updateConstraints method is overridden in the UIView subclass and must call super at the end of the method.

- (void)updateConstraints
{
    // Update some constraints

    [super updateConstraints];
}

The AutoLayout Guide leaves much to be desired but it is worth reading. I myself am using this as part of a UISwitch that toggles a subview with a pair of UITextFields with a simple and subtle collapse animation (0.2 seconds long). The constraints for the subview are being handled in the UIView subclasses updateConstraints methods as described above.

Constraint Animations on iOS Apps using XCode and Swift, The animation is a very important element in the development of software, the position of views as needed to satisfy changes in constraints. Instead I now animate changing the relative priorities of the yellow and blue view constraints which is a lot simpler as well as being more efficient. Before autolayout if you wanted to move a view on screen you would manipulate the frame of the view to change either the origin or size.

Generally, you just need to update constraints and call layoutIfNeeded inside the animation block. This can be either changing the .constant property of an NSLayoutConstraint, adding remove constraints (iOS 7), or changing the .active property of constraints (iOS 8 & 9).

Sample Code:
[UIView animateWithDuration:0.3 animations:^{
    // Move to right
    self.leadingConstraint.active = false;
    self.trailingConstraint.active = true;

    // Move to bottom
    self.topConstraint.active = false;
    self.bottomConstraint.active = true;

    // Make the animation happen
    [self.view setNeedsLayout];
    [self.view layoutIfNeeded];
}];
Sample Setup:

Controversy

There are some questions about whether the constraint should be changed before the animation block, or inside it (see previous answers).

The following is a Twitter conversation between Martin Pilkington who teaches iOS, and Ken Ferry who wrote Auto Layout. Ken explains that though changing constants outside of the animation block may currently work, it's not safe and they should really be change inside the animation block. https://twitter.com/kongtomorrow/status/440627401018466305

Animation:

Sample Project

Here's a simple project showing how a view can be animated. It's using Objective C and animates the view by changing the .active property of several constraints. https://github.com/shepting/SampleAutoLayoutAnimation

How to add animation to your constraints - The Startup, Animating AutoLayout constraints is easy: // Change some constraints here, or inside the animation blockUIView.animate(withDuration: 1) { view. Constraint Specifies the defined action causes the constraint to be animated. Suppress If selected, suppresses the constraints. If a constraint is suppressed and the dialog box is launched, the icon is selected . Enable If selected, the constraints are enabled. If a constraint is enabled and. the dialog box is launched, the icon is selected.

// Step 1, update your constraint
self.myOutletToConstraint.constant = 50; // New height (for example)

// Step 2, trigger animation
[UIView animateWithDuration:2.0 animations:^{

    // Step 3, call layoutIfNeeded on your animated view's parent
    [self.view layoutIfNeeded];
}];

Animate AutoLayout Constraints Separately - aunnnn, I have a 2014 post in Objective-C, and quite a bit has changed since then UIView.animate(withDuration: 0.25) { // Make all constraint changes  As of version constraint-layout:1.0.2, if you dynamically change a layout based attribute for an element inside ConstraintLayout (e.g. translationY), the animation won’t take the updated attribute into account. This means that when the animation is run, the attribute will instantly revert back to the original value, and then animate to the new value.

Swift 4 solution

UIView.animate

Three simple steps:

  1. Change the constraints, e.g.:

    heightAnchor.constant = 50
    
  2. Tell the containing view that its layout is dirty and that the autolayout should recalculate the layout:

    self.view.setNeedsLayout()
    
  3. 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 = 50
self.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 immediately
self.view.layoutIfNeeded()

heightAnchor.constant = 50
self.view.setNeedsLayout()
UIView.animate(withDuration: 0.5) {
    self.view.layoutIfNeeded()
}

UIViewPropertyAnimator

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 = 50
self.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 it
let 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 animator
heightAnchor.constant = 50
self.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.

How to Animate Auto Layout Constraints (2019), The gist is that you modify the constraints in your view, either by changing the constant property, creating or deleting constraints, and calling  With geometry constraint, imagine what you can do. You can animate: a ball rolling off a hand, a teardrop flowing down the face, a roller coaster ride, a sledge sliding across uneven snow surfaces, and; a person stringing a bead necklace. The possibilities are endless! The normal constraint and the tangent constraint build on the geometry constrain. They help to orient the child object and are typically used with the geometry constraint.

Beyond Constraints: Crafting Advanced iOS Animations with Auto , Old style, I set the frame in an animation block. New style, I have a IBOutlet to the auto-layout constraint which determines the Y position, in this  I know that you can easily change the constant value of the constraint in a [UIView animateWithDuration..] block to animate the height change. You need to create an IBOutlet for that constraint and hook it up in your xib, or otherwise keep a reference to it if you created it in code (or loop through all constraints to look for it).

How to Animate with Auto Layout Constraints, I've been looking for the simplest way to animate an object with ConstraintLayout from point A to point B, with the ability to change its duration and acceleration speed. E.g., moving a layout/view from off screen bottom to it's intended position with constraints set on screen.

Animating Constraint Contants, j'apprécie la réponse, mais je pense qu'il serait bien de prendre un peu plus loin. l'animation de bloc de base de la documentation [containerView layoutIfNeeded]; // Ensures that all pending layout operations have been completed [UIView animateWithDuration:1.0 animations:^{ // Make all constraint changes here [containerView layoutIfNeeded]; // Forces the layout of the subtree animation

Comments
  • I've never seen so many votes offered for a question and answer on a typo on SO before
  • If there is a typo in the answer, you should edit the answer. That's why they're editable.
  • @jeffamaphone - It would be more useful if you pointed out the typo so I knew where the mistake was. You could edit the answer yourself and fixed the typo saving everyone else our diatribe. I did just edit it to remove the constant from the animation block, if that's what you were referring to.
  • I don't know what the typo is. I was responding to comments above.
  • Then the typo is the question. Stupidly I was typing "setNeedsLayout" instead of "layoutIfNeeded". It's shown clearly in my question when I cut and paste my code with the error and the screenshots with the correct command. Yet couldn't seem to notice it til someone pointed it out.
  • You know what... your answer works. The WWDC works.... my vision fails. For some reason it took me a week to realize I was calling setNeedsLayout instead of layoutIfNeeded. I'm slightly horrified by how many hours I spent not noticing I just just typed the wrong method name.
  • The solution works but you don't need to change the constraint constant within the animation block. It's totally fine to set the constraint once before kicking off the animation. You should edit your answer.
  • This didn't work for me initially and then I realized you need to call layoutIfNeeded on the PARENT view, not the view the constraints apply to.
  • using layoutIfNeeded will animate all the subview refreshes not just the constraint change. how do u animate the constraint change only?
  • "Apple actually recommends you call it once before the animation block to ensure that all pending layout operations have been completed", thank you, never thought about that, but it makes sense.