我正在更新一个带有AdBannerView的旧应用程序,当没有广告时,它就会滑出屏幕。当有广告时,它就会在屏幕上滑动。基本的东西。

旧的风格,我设置帧在一个动画块。 新样式,我有一个IBOutlet到自动布局约束,它决定了Y的位置,在这种情况下,它是从父视图底部的距离,并修改常数:

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

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

横幅的移动和预期的完全一样,但是没有动画。


更新:我重新看了WWDC 12演讲《掌握自动布局的最佳实践》,其中包括动画。它讨论了如何使用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;
}

另一方面,我已经检查了很多次,这是在主线程上执行的。


当前回答

两个重要注意事项:

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 (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)

试试这个:

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

其他回答

Swift 4解决方案

UIView.animate

三个简单的步骤:

改变约束条件,例如: heightAnchor。常数= 50 告诉包含视图它的布局是脏的,并且自动布局应该重新计算布局: self.view.setNeedsLayout () 在动画块中告诉布局重新计算布局,这相当于直接设置帧(在这种情况下,自动布局将设置帧): UIView。animate(withDuration: 0.5) { self.view.layoutIfNeeded () }

最简单的例子:

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

旁注

有一个可选的第0步-在改变约束之前,你可能想要调用self.view.layoutIfNeeded()来确保动画的起点来自应用了旧约束的状态(以防有一些其他的约束变化不应该包含在动画中):

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

因为在iOS 10中我们获得了一个新的动画机制——UIViewPropertyAnimator,我们应该知道基本上相同的机制适用于它。步骤基本相同:

heightAnchor.constant = 50
self.view.setNeedsLayout()
let animator = UIViewPropertyAnimator(duration: 0.5, timingParameters: UICubicTimingParameters(animationCurve: .linear))
animator.addAnimations {
    self.view.layoutIfNeeded()
}
animator.startAnimation()

由于animator是动画的封装,我们可以保持对它的引用,并在以后调用它。然而,由于在动画块中我们只是告诉自动布局重新计算帧,我们必须在调用startAnimation之前改变约束。因此,这样的事情是可能的:

// 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()

改变约束和启动动画器的顺序很重要——如果我们只是改变约束并将动画器留到后面的某个点,下一个重绘周期可以调用自动布局重新计算,而更改将不会被动画化。

另外,请记住单个动画器是不可重用的——一旦运行它,就不能“重新运行”它。所以我想没有什么好的理由继续使用动画器,除非我们用它来控制交互式动画。

我试着将Constraints动画化,但很难找到一个好的解释。

其他答案所说的是完全正确的:你需要调用[self。视图layoutIfNeeded];inside animateWithDuration: animations:。然而,另一个重要的点是为每个你想要动画的NSLayoutConstraint设置指针。

我在GitHub上创建了一个例子。

工作方案100% Swift 5.3

我已经阅读了所有的答案,并希望分享我在所有应用程序中使用的代码和线条层次结构,以正确地动画它们,这里的一些解决方案不起作用,您应该在较慢的设备上检查它们,例如iPhone 5。

self.btnHeightConstraint.constant = 110
UIView.animate(withDuration: 0.27) { [weak self] in 
     self?.view.layoutIfNeeded()
}

迅速的解决方案:

yourConstraint.constant = 50
UIView.animate(withDuration: 1.0, animations: {
    yourView.layoutIfNeeded
})

通常,你只需要更新约束并在动画块中调用layoutIfNeeded。这可以是改变NSLayoutConstraint的.constant属性,添加remove约束(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];
}];

示例设置:

争议

有一些关于约束是否应该在动画块之前或里面改变的问题(见以前的答案)。

以下是教授iOS的马丁·皮尔金顿(Martin Pilkington)和自动布局(Auto Layout)作者肯·费里(Ken Ferry)在Twitter上的对话。Ken解释说,虽然在动画块之外改变常量目前可能工作,但这是不安全的,它们应该在动画块内部改变。 https://twitter.com/kongtomorrow/status/440627401018466305

动画:

示例项目

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