我正在更新一个带有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;
}

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


当前回答

工作方案100% Swift 5.3

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

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

其他回答

在约束动画的上下文中,我想提一下在keyboard_opened通知中立即对约束进行动画的特定情况。

约束定义了一个从文本框到容器顶部的顶部空间。打开键盘时,我只需将常数除以2。

我无法直接在键盘通知中实现一致的平滑约束动画。大约一半的时间视图会跳转到它的新位置——没有动画。

在我看来,可能会有一些额外的布局发生的结果,键盘打开。 添加一个简单的dispatch_after块和10ms的延迟使得动画每次都运行-没有跳跃。

有一篇文章谈到了这一点: http://weblog.invasivecode.com/post/42362079291/auto-layout-and-core-animation-auto-layout-was

在信中,他是这样写的:

- (void)handleTapFrom:(UIGestureRecognizer *)gesture {
    if (_isVisible) {
        _isVisible = NO;
        self.topConstraint.constant = -44.;    // 1
        [self.navbar setNeedsUpdateConstraints];  // 2
        [UIView animateWithDuration:.3 animations:^{
            [self.navbar layoutIfNeeded]; // 3
        }];
    } else {
        _isVisible = YES;
        self.topConstraint.constant = 0.;
        [self.navbar setNeedsUpdateConstraints];
        [UIView animateWithDuration:.3 animations:^{
            [self.navbar layoutIfNeeded];
        }];
    }
}

希望能有所帮助。

使用Xcode 8.3.3测试Swift 3的解决方案:

self.view.layoutIfNeeded()
self.calendarViewHeight.constant = 56.0

UIView.animate(withDuration: 0.5, delay: 0.0, options: UIViewAnimationOptions.curveEaseIn, animations: {
        self.view.layoutIfNeeded()
    }, completion: nil)

只要记住那个自我。calendarViewHeight是一个引用自customView (CalendarView)的约束。我在self上调用了. layoutifneeded()。view和NOT在self。calendarview上

希望这对你有所帮助。

我很感激你提供的答案,但我认为最好能进一步讨论这个问题。

文档中的基本块动画

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

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

两个重要注意事项:

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