我如何animation约束更改?

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

旧的风格,我在一个animation块中设置框架。 新的风格,我有一个IBOutlet决定Y位置的约束,在这种情况下,它是从超视图底部的距离,并修改常数。

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

横幅移动,正如所料,但没有animation。

更新:我重新观看了包含animation的WWDC12video“ 掌握自动布局的最佳实践 ”。 它讨论了如何使用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. 您需要在animation块中调用layoutIfNeeded 。 苹果实际上build议你在animation块之前调用一次,以确保所有挂起的布局操作都已完成

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

尝试这个:

Objective-C的

 - (void)moveBannerOffScreen { [self.view layoutIfNeeded]; _addBannerDistanceFromBottomConstraint.constant = -32; [UIView animateWithDuration:5 animations:^{ [self.view layoutIfNeeded]; // Called on parent view }]; bannerIsVisible = FALSE; } - (void)moveBannerOnScreen { [self.view layoutIfNeeded]; _addBannerDistanceFromBottomConstraint.constant = 0; [UIView animateWithDuration:5 animations:^{ [self.view layoutIfNeeded]; // Called on parent view }]; bannerIsVisible = TRUE; } 

Swift 3

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

我很欣赏提供的答案,但我认为这将是更好的进一步。

文档中的基本块animation

 [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方法来animation子视图约束呢?

调用子视图updateConstraints方法的animation块

 [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一部分,用一个简单的和细微的崩溃animation(0.2秒长)切换与一对UITextField子视图。 如上所述,子视图的约束在UIView的子类updateConstraints方法中被处理。

通常,您只需更新约束并在animation块内调用layoutIfNeeded 。 这可以是更改.constant属性,添加删除约束(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项目如此示例动画项目。

争议

关于约束是应该在animation块之前被改变还是内部有一些问题(见前面的答案)。

以下是教授iOS的Martin Pilkington和编写Auto Layout的Ken Ferry之间的Twitter对话。 Ken解释说,尽pipe改变animation块之外的常量现在可以工作,但是这并不安全,他们应该真的在animation块内部进行更改。 https://twitter.com/kongtomorrow/status/440627401018466305

animation:

示例项目

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

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

故事板,代码,技巧和几个问题

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

把你想要的约束变成类variables来保存一个强有力的参考。 在Swift中我使用了懒惰variables:

 lazy var centerYInflection:NSLayoutConstraint = { let temp = self.view.constraints.filter({ $0.firstItem is MNGStarRating }).filter ( { $0.secondItem is UIWebView }).filter({ $0.firstAttribute == .CenterY }).first return temp! }() 

经过一番实验后,我注意到一个人必须从视图ABOVE (也就是superview)中获得约束条件被定义的两个视图的约束。 在下面的例子中(MNGStarRating和UIWebView都是我创build一个约束之间的两种types的项目,它们是self.view中的子视图)。

过滤链接

我利用Swift的过滤方法来分离将作为拐点的期望约束。 人们也可以变得更复杂,但filter在这里做得不错。

使用Swiftanimation约束

Nota Bene – 这个例子是故事板/代码解决scheme,并且假设在故事板中已经设定了默认限制。 然后可以使用代码来animation更改。

假设你创build一个属性来过滤准确的标准,并得到一个特定的转折点为您的animation(当然,如果你需要多个约束,你也可以筛选一个数组并循环):

 lazy var centerYInflection:NSLayoutConstraint = { let temp = self.view.constraints.filter({ $0.firstItem is MNGStarRating }).filter ( { $0.secondItem is UIWebView }).filter({ $0.firstAttribute == .CenterY }).first return temp! }() 

….

一段时间之后…

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

许多错误的转折

这些笔记实际上是我为自己写的一套技巧。 我亲自和痛苦地做了所有的事情。 希望本指南可以省掉其他人。

  1. 注意zPositioning。 有时候什么都没有发生,你应该隐藏一些其他的视图或者使用视图debugging器来定位你的animation视图。 我甚至发现用户定义的运行时属性在故事板的xml中丢失,并导致animation视图被覆盖(工作时)的情况。

  2. 总是花一点时间阅读文档(新旧),快速帮助和标题。 为了更好地pipe理AutoLayout约束,Apple不断进行大量更改(请参阅堆栈视图)。 或者至less是AutoLayout食谱 。 请记住,有时最好的解决scheme是在旧的文档/video。

  3. 玩animation中的值,并考虑使用其他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,除非你真的在代码中添加一个新的约束。 我发现大多数情况下,我在故事板中布置了具有所需约束的视图(将视图放在屏幕外),然后在代码中为animation板中以前创build的约束移动视图。
  10. 我花了很多时间用新的NSAnchorLayout类和子类来构build约束。 这些工作很好,但花了我一段时间才意识到,我需要的所有约束已经存在于故事板中。 如果你在代码中构build约束,那么肯定会使用这个方法来聚合你的约束:

使用情节串连图板时,AVOID解决scheme的快速示例

 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({ ($0.firstItem is UIButton || $0.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 }() 

如果您忘记了其中的一个提示或更简单的提示,比如添加layoutIfNeeded,那么很可能什么也不会发生:在这种情况下,您可能会遇到这样一个半熟的解决scheme:

NB – 花点时间阅读下面的AutoLayout部分和原始指南。 有一种方法可以使用这些技术来补充dynamicanimation师。

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

从AutoLayout指南摘录(注意第二个片段是用于使用OS X)。 顺便说一句 – 就我所知,这已经不在当前的指南中了。 首选的技术不断发展。

animation自动布局所做的更改

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

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

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

在OS X中,使用层次支持的animation时请使用以下代码:

 [containterView layoutSubtreeIfNeeded]; [NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) { [context setAllowsImplicitAnimation: YES]; // Make all constraint changes here [containerView layoutSubtreeIfNeeded]; }]; 

如果不使用分层animation,则必须使用约束的animation制作animation:

 [[constraint animator] setConstant:42]; 

对于那些更好地视觉学习的人来说,请查看苹果公司早期的video

密切关注

通常在文档中有小的注释或代码片断,可以产生更大的想法。 例如,将自动布局约束附加到dynamicanimation师是一个很大的想法。

祝你好运,愿军队与你同在。

我试图使Constraintsanimation,并不容易find一个好的解释。

还有什么其他答案是完全正确的:你需要调用[self.view layoutIfNeeded];animateWithDuration: animations:里面animateWithDuration: animations: 。 然而,另一个重要的点是要为每一个你想要animation的NSLayoutConstraint指针。

我在GitHub中创build了一个例子 。

Swift解决scheme:

 yourConstraint.constant = 50 UIView.animateWithDuration(1, animations: yourView.layoutIfNeeded) 

工作解决scheme100% Swift 3.1

我已阅读所有的答案,并希望分享我所有的应用程序中使用的代码和层次结构,以正确地为它们设置animation效果。这里的一些解决scheme不起作用,您应该在较慢的设备(例如iPhone 5)上检查它们。

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

有一篇文章谈到这个: 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工作并testing了Swift 3的解决scheme:

 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) 

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

希望这个帮助。

在约束animation的上下文中,我想提到一个特定的情况,我在一个keyboard_opened通知中立即animation约束。

约束定义了从文本字段到容器顶部的顶部空间。 在键盘打开时,我只是把常数除以2。

我无法直接在键盘通知中实现一致的顺畅约束animation。 大约一半的时间观点只是跳到新的位置 – 没有animation。

发生在我身上的可能是由于键盘打开而发生的一些额外的布局。 添加一个简单的dispatch_after块,延迟10ms使animation每次都运行 – 不跳跃。