故事板,代码,技巧和一些陷阱
其他答案也很好,但是这个例子使用最近的例子强调了一些非常重要的动画约束技巧。在意识到以下几点之前,我经历了很多变化:
将要定位的约束放入Class变量中,以保留强引用。在Swift中,我使用了惰性变量:
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(又名上海华),其中该约束所限定的两个视图。在下面的示例中(MNGStarRating和UIWebView都是我要在它们之间创建约束的两种类型的项目,它们都是self.view中的子视图)。
过滤链
我利用Swift的filter方法来分离所需的约束,该约束将用作拐点。一个人也可能变得更加复杂,但是过滤器在这里做得很好。
使用Swift动画约束
Nota Bene-此示例是情节提要/代码解决方案,并假定在情节提要中设置了默认约束。然后可以使用代码为更改制作动画。
假设您创建了一个属性,以使用准确的条件进行过滤并为动画指定特定的拐点(当然,您也可以过滤数组并在需要多个约束时循环遍历):
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
}
}
}
许多错误的转折
这些笔记实际上是我为自己写的一组技巧。我亲自做了所有不该做的事。希望本指南可以帮助其他人。
注意zPositioning。有时,当显然什么都没发生时,您应该隐藏其他一些视图或使用视图调试器来定位动画视图。我什至发现了一些情况,即用户定义的运行时属性在情节提要的xml中丢失,并导致动画视图被覆盖(工作时)。
总是花一点时间阅读文档(新旧),快速帮助和标题。Apple不断进行大量更改以更好地管理AutoLayout约束(请参阅堆栈视图)。或至少是AutoLayout Cookbook。请记住,有时最好的解决方案是在较早的文档/视频中。
播放动画中的值,并考虑使用其他animateWithDuration变体。
不要将特定的布局值硬编码为确定对其他常量进行更改的标准,而应使用允许您确定视图位置的值。CGRectContainsRect
是一个例子
- 如果需要,请不要犹豫地使用与参与约束定义的视图关联的布局边距
let viewMargins = self.webview.layoutMarginsGuide
:上的例子
- 不要做您不需要做的工作,所有在情节提要上受约束的视图都将约束附加到属性self.viewName.constraints
- 将所有约束的优先级更改为小于1000。我在情节提要上将我的优先级设置为250(低)或750(高)。(如果您尝试将代码中的1000优先级更改为任何优先级,则该应用将崩溃,因为需要1000)
- 考虑不要立即尝试使用activateConstraints和deactivateConstraints(它们有其位置,但是当刚学习或如果您使用情节提要时,使用这些可能意味着您做得太多了-尽管它们确实有位置,如下所示)
- 除非您确实在代码中添加了新约束,否则请考虑不使用addConstraints / removeConstraints。我发现,大多数情况下,我会使用所需的约束在情节提要中布局视图(将视图放置在屏幕外),然后在代码中,对先前在情节提要中创建的约束进行动画处理以移动视图。
- 我花了很多时间在新的NSAnchorLayout类和子类上建立约束。这些工作很好,但是我花了一段时间才意识到我需要的所有约束已经存在于情节提要中。如果您在代码中构建约束,那么肯定可以使用此方法来聚合约束:
使用情节提要时AVOID解决方案的快速样本
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),则很可能什么都不会发生:在这种情况下,您可能会遇到这样的问题:
注意:请花一点时间阅读下面的“自动版式”部分和原始指南。有一种方法可以使用这些技术来补充动态动画师。
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
}
}
自动版式指南中的代码段(请注意,第二个代码段适用于OS X)。顺便说一句-据我所知,这不再是当前指南中的内容。 首选技术不断发展。
通过自动版面制作动画更改
如果您需要对“自动版式”所做的动画更改进行完全控制,则必须以编程方式进行约束更改。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中,使用支持图层的动画时,请使用以下代码:
[containterView layoutSubtreeIfNeeded];
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
[context setAllowsImplicitAnimation: YES];
// Make all constraint changes here
[containerView layoutSubtreeIfNeeded];
}];
当您不使用支持图层的动画时,必须使用约束的动画师为常量设置动画:
[[constraint animator] setConstant:42];
对于那些视觉效果更好的人,请查看Apple的早期视频。
密切关注
通常在文档中有一些小的注释或代码段会带来更大的构想。例如,将自动布局约束附加到动态动画师是一个好主意。
祝你好运,愿原力与你同在。