协慌网

登录 贡献 社区

如何为约束更改设置动画?

我正在使用AdBannerView更新旧应用,当没有广告时,它会从屏幕上滑落。当有广告时,它会在屏幕上滑动。基本的东西。

旧样式,我在动画块中设置框架。新风格,我有一个确定 Y 位置的约束的IBOutlet ,在这种情况下它是与 superview 底部的距离,并修改常量。

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

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

横幅移动,完全符合预期,但没有动画。

更新:我重新观看了 WWDC12 视频 “ 掌握自动布局的最佳实践 ”,其中包括动画。它讨论了如何使用CoreAnimation更新约束。

在此输入图像描述在此输入图像描述

我尝试使用以下代码,但得到完全相同的结果。

- (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;
}

在旁注中,我已经多次检查过,这是在主线程上执行的。

答案

两个重要说明:

  1. 您需要在动画块中调用layoutIfNeeded 。 Apple 实际上建议您在动画块之前调用一次,以确保所有挂起的布局操作都已完成

  2. 您需要在父视图 (例如self.view )上专门调用它,而不是具有附加约束的子视图。这样做会更新所有受约束的视图,包括动画可能被约束到您更改约束的视图的其他视图(例如,视图 B 附加到视图 A 的底部,您刚刚更改了视图 A 的顶部偏移,您希望视图 B 用它来制作动画)

试试这个:

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;
}

斯威夫特 3

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

我很欣赏所提供的答案,但我认为进一步采取它会很好。

文档中的基本块动画

[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
}];

但实际上这是一个非常简单的场景。如果我想通过updateConstraints方法为子视图约束设置动画怎么办?

调用子视图 updateConstraints 方法的动画块

[self.view layoutIfNeeded];
[self.subView setNeedsUpdateConstraints];
[self.subView updateConstraintsIfNeeded];
[UIView animateWithDuration:1.0f delay:0.0f options:UIViewAnimationOptionLayoutSubviews animations:^{
    [self.view layoutIfNeeded];
} completion:nil];

updateConstraints 方法在 UIView 子类中被重写,并且必须在方法结束时调用 super。

- (void)updateConstraints
{
    // Update some constraints

    [super updateConstraints];
}

AutoLayout 指南还有很多不足之处,但值得一读。我自己使用它作为UISwitch一部分,用一对UITextField切换子视图,并带有一个简单而细微的折叠动画(0.2 秒长)。如上所述,在 UIView 子类 updateConstraints 方法中处理子视图的约束。

通常,您只需更新约束并在动画块内调用layoutIfNeeded 。这可以是改变.constant一个的属性NSLayoutConstraint ,加入除去约束(iOS 的 7),或改变.active约束的属性(的 iOS 8 和 9)。

示例代码:

[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];
}];

样本设置:

Xcode Project如此示例动画项目。

争议

关于是否应该在动画块之前或其内部更改约束存在一些问题(请参阅前面的答案)。

以下是教授 iOS 的 Martin Pilkington 和编写 Auto Layout 的 Ken Ferry 之间的 Twitter 对话。 Ken 解释说,尽管在动画块之外更改常量当前可能有效,但它并不安全,它们应该在动画块内部进行更改。 https://twitter.com/kongtomorrow/status/440627401018466305

动画:

示例项目

这是一个简单的项目,展示了如何动画视图。它使用 Objective C 并通过更改多个约束的.active属性来动画化视图。 https://github.com/shepting/SampleAutoLayoutAnimation