ios 如何动画约束更改?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12622424/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 20:26:32  来源:igfitidea点击:

How do I animate constraint changes?

iosobjective-canimationautolayoutios6

提问by DBD

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

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

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

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

- (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 noanimation.

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



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

更新:我重新观看了涵盖动画的WWDC 12 演讲“掌握自动布局的最佳实践”。它讨论了如何使用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 mainthread.

顺便说一句,我已经检查了很多次,这是在线程上执行的。

回答by g3rv4

Two important notes:

两个重要的注意事项:

  1. You need to call layoutIfNeededwithin 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 allconstrained 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)

  1. 您需要layoutIfNeeded在动画块内调用。苹果其实建议你在动画块之前调用一次,以确保所有挂起的布局操作都已经完成

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

Try this:

尝试这个:

Objective-C

目标-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

斯威夫特 3

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

回答by Cameron Lowell Palmer

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 updateConstraintsmethod?

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

An animation block that calls the subviews updateConstraints method

一个调用子视图 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];

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

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

- (void)updateConstraints
{
    // Update some constraints

    [super updateConstraints];
}

The AutoLayout Guideleaves much to be desired but it is worth reading. I myself am using this as part of a UISwitchthat 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.

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

回答by Steven Hepting

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

通常,您只需要更新约束并layoutIfNeeded在动画块内部调用。这可以是更改.constantan的属性NSLayoutConstraint,添加删除约束(iOS 7),或更改.active约束的属性(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 so sample animation project.

Xcode Project 示例动画项目。

Controversy

争议

There are some questions about whether the constraint should be changed beforethe animation block, or insideit (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 currentlywork, it's not safe and they should really be change insidethe animation block. https://twitter.com/kongtomorrow/status/440627401018466305

以下是教授 iOS 的 Martin Pilkington 和编写 Auto Layout 的 Ken Ferry 之间的 Twitter 对话。Ken 解释说,虽然在动画块之外更改常量目前可能有效,但它并不安全,它们实际上应该动画块更改。 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 .activeproperty of several constraints. https://github.com/shepting/SampleAutoLayoutAnimation

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

回答by John Erck

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

回答by Milan Nosá?

Swift 4 solution

斯威夫特 4 解决方案

UIView.animate

UIView.animate

Three simple steps:

三个简单的步骤:

  1. Change the constraints, e.g.:

    heightAnchor.constant = 50
    
  2. Tell the containing viewthat 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()
    }
    
  1. 更改约束,例如:

    heightAnchor.constant = 50
    
  2. 告诉包含view它的布局是脏的,并且自动布局应该重新计算布局:

    self.view.setNeedsLayout()
    
  3. 在动画块中告诉布局重新计算布局,这相当于直接设置帧(在这种情况下,自动布局将设置帧):

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

有一个可选的第 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

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:

由于在 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()

Since animatoris 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:

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

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.

另外,请记住单个动画师是不可重用的——一旦你运行它,你就不能“重新运行”它。所以我想没有一个很好的理由让动画师留在身边,除非我们用它来控制交互式动画。

回答by Tommie C.

Storyboard, Code, Tips and a few Gotchas

故事板、代码、提示和一些陷阱

The other answers are just fine but this one highlights a few fairly important gotchas of animating constraints using a recent example. I went through a lot of variations before I realized the following:

其他答案很好,但这个答案使用最近的示例突出了动画约束的一些相当重要的问题。在我意识到以下内容之前,我经历了很多变化:

Make the constraints you want to target into Class variables to hold a strong reference. In Swift I used lazy variables:

将您想要定位的约束放入 Class 变量以保持强引用。在 Swift 中,我使用了惰性变量:

lazy var centerYInflection:NSLayoutConstraint = {
       let temp =  self.view.constraints.filter({ 
lazy var centerYInflection:NSLayoutConstraint = {
    let temp =  self.view.constraints.filter({ 
@IBAction func toggleRatingView (sender:AnyObject){

    let aPointAboveScene = -(max(UIScreen.mainScreen().bounds.width,UIScreen.mainScreen().bounds.height) * 2.0)

    self.view.layoutIfNeeded()


    //Use any animation you want, I like the bounce in springVelocity...
    UIView.animateWithDuration(1.0, delay: 0.0, usingSpringWithDamping: 0.3, initialSpringVelocity: 0.75, options: [.CurveEaseOut], animations: { () -> Void in

        //I use the frames to determine if the view is on-screen
        if CGRectContainsRect(self.view.frame, self.ratingView.frame) {

            //in frame ~ animate away
            //I play a sound to give the animation some life

            self.centerYInflection.constant = aPointAboveScene
            self.centerYInflection.priority = UILayoutPriority(950)

        } else {

            //I play a different sound just to keep the user engaged
            //out of frame ~ animate into scene
            self.centerYInflection.constant = 0
            self.centerYInflection.priority = UILayoutPriority(950)
            self.view.setNeedsLayout()
            self.view.layoutIfNeeded()
         }) { (success) -> Void in

            //do something else

        }
    }
}
.firstItem is MNGStarRating }).filter ( {
private var _nc:[NSLayoutConstraint] = []
    lazy var newConstraints:[NSLayoutConstraint] = {

        if !(self._nc.isEmpty) {
            return self._nc
        }

        let viewMargins = self.webview.layoutMarginsGuide
        let minimumScreenWidth = min(UIScreen.mainScreen().bounds.width,UIScreen.mainScreen().bounds.height)

        let centerY = self.ratingView.centerYAnchor.constraintEqualToAnchor(self.webview.centerYAnchor)
        centerY.constant = -1000.0
        centerY.priority = (950)
        let centerX =  self.ratingView.centerXAnchor.constraintEqualToAnchor(self.webview.centerXAnchor)
        centerX.priority = (950)

        if let buttonConstraints = self.originalRatingViewConstraints?.filter({

            (
UIView.animateWithDuration(1.0, delay: 0.0, usingSpringWithDamping: 0.3, initialSpringVelocity: 1.0, options: [.CurveEaseOut], animations: { () -> Void in

            //
            if self.starTopInflectionPoint.constant < 0  {
                //-3000
                //offscreen
                self.starTopInflectionPoint.constant = self.navigationController?.navigationBar.bounds.height ?? 0
                self.changeConstraintPriority([self.starTopInflectionPoint], value: UILayoutPriority(950), forView: self.ratingView)

            } else {

                self.starTopInflectionPoint.constant = -3000
                 self.changeConstraintPriority([self.starTopInflectionPoint], value: UILayoutPriority(950), forView: self.ratingView)
            }

        }) { (success) -> Void in

            //do something else
        }

    }
.firstItem is UIButton ||
[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
}];
.secondItem is UIButton ) }) { self._nc.appendContentsOf(buttonConstraints) } self._nc.append( centerY) self._nc.append( centerX) self._nc.append (self.ratingView.leadingAnchor.constraintEqualToAnchor(viewMargins.leadingAnchor, constant: 10.0)) self._nc.append (self.ratingView.trailingAnchor.constraintEqualToAnchor(viewMargins.trailingAnchor, constant: 10.0)) self._nc.append (self.ratingView.widthAnchor.constraintEqualToConstant((minimumScreenWidth - 20.0))) self._nc.append (self.ratingView.heightAnchor.constraintEqualToConstant(200.0)) return self._nc }()
.secondItem is UIWebView }).filter({
[containterView layoutSubtreeIfNeeded];
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
     [context setAllowsImplicitAnimation: YES];
     // Make all constraint changes here
     [containerView layoutSubtreeIfNeeded];
}];
.firstAttribute == .CenterY }).first return temp! }()
.firstItem is MNGStarRating }).filter ( {
[[constraint animator] setConstant:42];
.secondItem is UIWebView }).filter({
yourConstraint.constant = 50
UIView.animate(withDuration: 1.0, animations: {
    yourView.layoutIfNeeded
})
.firstAttribute == .CenterY }).first return temp! }()

After some experimentation I noted that one MUST obtain the constraint from the view ABOVE(aka the superview) the two views where the constraint is defined. In the example below (both MNGStarRating and UIWebView are the two types of items I am creating a constraint between, and they are subviews within self.view).

经过一些实验,我注意到必须从定义约束的两个视图ABOVE(又名超级视图)中获取约束。在下面的示例中(MNGStarRating 和 UIWebView 都是我在两者之间创建约束的两种类型的项目,它们是 self.view 中的子视图)。

Filter Chaining

过滤器链接

I take advantage of Swift's filter method to separate the desired constraint that will serve as the inflection point. One could also get much more complicated but filter does a nice job here.

我采取迅速的过滤方法分离所需的约束,将作为拐点的优势。一个也可能变得更复杂,但过滤器在这里做得很好。

Animating Constraints Using Swift

使用 Swift 动画约束

Nota Bene - This example is the storyboard/code solution and assumes one has made default constraints in the storyboard. One can then animate the changes using code.

Nota Bene - 此示例是故事板/代码解决方案,并假设在故事板中设置了默认约束。然后可以使用代码为更改设置动画。

Assuming you create a property to filter with accurate criteria and get to a specific inflection point for your animation (of course you could also filter for an array and loop through if you need multiple constraints):

假设您创建了一个属性以使用准确的标准进行过滤并到达动画的特定拐点(当然,如果您需要多个约束,您也可以过滤数组并循环):

self.view.layoutIfNeeded() // Force lays of all subviews on root view
UIView.animate(withDuration: 0.5) { [weak self] in // allowing to ARC to deallocate it properly
       self?.tbConstraint.constant = 158 // my constraint constant change
       self?.view.layoutIfNeeded() // Force lays of all subviews on root view again.
}

....

....

Sometime later...

一段时间以后...

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)

The many wrong turns

许多错误的转弯

These notes are really a set of tips that I wrote for myself. I did all the don'ts personally and painfully. Hopefully this guide can spare others.

这些笔记实际上是我为自己写的一组提示。我个人和痛苦地做了所有不该做的事情。希望本指南可以帮助其他人。

  1. Watch out for zPositioning. Sometimes when nothing is apparently happening, you should hide some of the other views or use the view debugger to locate your animated view. I've even found cases where a User Defined Runtime Attribute was lost in a storyboard's xml and led to the animated view being covered (while working).

  2. Always take a minute to read the documentation (new and old), Quick Help, and headers. Apple keeps making a lot of changes to better manage AutoLayout constraints (see stack views). Or at least the AutoLayout Cookbook. Keep in mind that sometimes the best solutions are in the older documentation/videos.

  3. Play around with the values in the animation and consider using other animateWithDuration variants.

  4. Don't hardcode specific layout values as criteria for determining changes to other constants, instead use values that allow you to determine the location of the view. CGRectContainsRectis one example

  5. If needed, don't hesitate to use the layout margins associated with a view participating in the constraint definition let viewMargins = self.webview.layoutMarginsGuide: is on example
  6. Don't do work you don't have to do, all views with constraints on the storyboard have constraints attached to the property self.viewName.constraints
  7. Change your priorities for any constraints to less than 1000. I set mine to 250 (low) or 750 (high) on the storyboard; (if you try to change a 1000 priority to anything in code then the app will crash because 1000 is required)
  8. Consider not immediately trying to use activateConstraints and deactivateConstraints (they have their place but when just learning or if you are using a storyboard using these probably means your doing too much ~ they do have a place though as seen below)
  9. Consider not using addConstraints / removeConstraints unless you are really adding a new constraint in code. I found that most times I layout the views in the storyboard with desired constraints (placing the view offscreen), then in code, I animate the constraints previously created in the storyboard to move the view around.
  10. I spent a lot of wasted time building up constraints with the new NSAnchorLayout class and subclasses. These work just fine but it took me a while to realize that all the constraints that I needed already existed in the storyboard. If you build constraints in code then most certainly use this method to aggregate your constraints:
  1. 注意 zPositioning。有时,当什么都没有发生时,您应该隐藏一些其他视图或使用视图调试器来定位您的动画视图。我什至发现用户定义的运行时属性在故事板的 xml 中丢失并导致动画视图被覆盖(在工作时)的情况。

  2. 始终花一分钟时间阅读文档(新的和旧的)、快速帮助和标题。Apple 不断进行大量更改以更好地管理 AutoLayout 约束(请参阅堆栈视图)。或者至少是AutoLayout Cookbook。请记住,有时最好的解决方案是在较旧的文档/视频中。

  3. 使用动画中的值并考虑使用其他 animateWithDuration 变体。

  4. 不要将特定的布局值硬编码为确定其他常量更改的标准,而是使用允许您确定视图位置的值。CGRectContainsRect是一个例子

  5. 如果需要,请毫不犹豫地使用与参与约束定义的视图关联的布局边距 let viewMargins = self.webview.layoutMarginsGuide:例如
  6. 不要做你不需要做的工作,故事板上有约束的所有视图都有附加到属性 self.viewName.constraints 的约束
  7. 将任何约束的优先级更改为小于 1000。我在故事板上将我的设置为 250(低)或 750(高);(如果您尝试将 1000 优先级更改为代码中的任何内容,则应用程序将崩溃,因为需要 1000)
  8. 考虑不要立即尝试使用 activateConstraints 和deactivateConstraints(它们有它们的位置,但是在刚学习时或者如果您正在使用故事板,使用这些可能意味着您做得太多了〜尽管如下所示,它们确实有位置)
  9. 考虑不要使用 addConstraints / removeConstraints ,除非您真的要在代码中添加新约束。我发现大多数时候我在故事板中使用所需的约束(将视图放置在屏幕外)布局视图,然后在代码中,我对之前在故事板中创建的约束进行动画处理以移动视图。
  10. 我浪费了很多时间来建立新的 NSanchorLayout 类和子类的约束。这些工作很好,但我花了一段时间才意识到我需要的所有约束都已经存在于故事板中。如果您在代码中构建约束,那么肯定会使用此方法来聚合您的约束:

Quick Sample Of Solutions to AVOID when using Storyboards

快速的样品溶液用故事板时避免

##代码##

If you forget one of these tips or the more simple ones such as where to add the layoutIfNeeded, most likely nothing will happen: In which case you may have a half baked solution like this:

如果您忘记了这些技巧之一或更简单的技巧,例如在何处添加 layoutIfNeeded,则很可能什么也不会发生:在这种情况下,您可能有这样的半成品解决方案:

NB - Take a moment to read the AutoLayout Section Below and the original guide. There is a way to use these techniques to supplement your Dynamic Animators.

注意 - 花点时间阅读下面的 AutoLayout 部分和原始指南。有一种方法可以使用这些技术来补充您的动态动画师。

##代码##

Snippet from the AutoLayout Guide (note the second snippet is for using OS X). BTW - This is no longer in the current guide as far as I can see.The preferred techniques continue to evolve.

AutoLayout Guide 中的片段(注意第二个片段是用于使用 OS X)。顺便说一句 - 就我所见,这不再在当前指南中。首选技术不断发展。

Animating Changes Made by Auto Layout

自动布局所做的动画更改

If you need full control over animating changes made by Auto Layout, you must make your constraint changes programmatically. The basic concept is the same for both iOS and OS X, but there are a few minor differences.

如果您需要完全控制自动布局所做的动画更改,则必须以编程方式更改约束。iOS 和 OS X 的基本概念是相同的,但有一些细微差别。

In an iOS app, your code would look something like the following:

在 iOS 应用程序中,您的代码如下所示:

##代码##

In OS X, use the following code when using layer-backed animations:

在 OS X 中,使用层支持动画时使用以下代码:

##代码##

When you aren't using layer-backed animations, you must animate the constant using the constraint's animator:

当您不使用层支持的动画时,您必须使用约束的动画制作器为常量设置动画:

##代码##

For those who learn better visually check out this early video from Apple.

对于那些在视觉上学习得更好的人,请查看Apple 的这个早期视频

Pay Close Attention

密切关注

Often in documentation there are small notes or pieces of code that lead to bigger ideas. For example attaching auto layout constraints to dynamic animators is a big idea.

通常在文档中会有一些小注释或代码片段,它们会导致更大的想法。例如,将自动布局约束附加到动态动画师是一个好主意。

Good Luck and May the Force be with you.

祝你好运,愿原力与你同在。

回答by Nazariy Vlizlo

Swift solution:

迅捷解决方案:

##代码##

回答by C0mrade

Working Solution 100%Swift 3.1

工作解决方案 100%Swift 3.1

i have read all the answers and want to share the code and hierarchy of lines which i have used in all my applications to animate them correctly, Some solutions here are not working, you should check them on slower devices e.g iPhone 5 at this moment.

我已阅读所有答案并想分享我在所有应用程序中使用的代码和行的层次结构以正确地为它们设置动画,这里的某些解决方案不起作用,此时您应该在较慢的设备(例如 iPhone 5)上检查它们。

##代码##

回答by Edoardo Vicoli

Working and just tested solution for Swift 3 with Xcode 8.3.3:

使用 Xcode 8.3.3 为 Swift 3 工作且刚刚测试的解决方案:

##代码##

Just keep in mind that self.calendarViewHeight is a constraint referred to a customView (CalendarView). I called the .layoutIfNeeded() on self.view and NOT on self.calendarView

请记住,self.calendarViewHeight 是一个引用 customView (CalendarView) 的约束。我在 self.view 上调用了 .layoutIfNeeded() 而不是在 self.calendarView

Hope this help.

希望这有帮助。

回答by Gabriel.Massana

I was trying to animate Constraints and was not really easy to found a good explanation.

我试图动画约束,并没有很容易找到一个很好的解释。

What other answers are saying is totally true: you need to call [self.view layoutIfNeeded];inside animateWithDuration: animations:. However, the other important point is to have pointers for every NSLayoutConstraintyou want to animate.

其他答案所说的完全正确:您需要致电[self.view layoutIfNeeded];inside animateWithDuration: animations:。然而,另一个重要的点是为每个NSLayoutConstraint你想要制作动画的人提供指针。

I created an example in GitHub.

我在 GitHub 中创建了一个示例