How do I animate constraint changes?

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 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 WWDC12 video "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:

  • 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

  • You need to call it specifically on the parent view (eg 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 (eg 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()
    }
    

    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 UITextField s 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.


    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:

    Xcode Project如此示例动画项目。

    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

    链接地址: http://www.djcxy.com/p/25890.html

    上一篇: 如何更改iOS 7中的状态栏文字颜色

    下一篇: 我如何动画约束更改?