故事板,代码,技巧和一些陷阱
其他答案都很好,但这一个用最近的例子强调了动画约束的一些相当重要的陷阱。我经历了很多变化,才意识到以下几点:
将目标约束设置为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!
}()
经过一些实验后,我注意到必须从上面的视图(又名superview)两个视图中获得约束,约束是定义的。在下例中(MNGStarRating和UIWebView都是我创建约束之间的两种类型的项,它们是self.view中的子视图)。
过滤器链
我利用Swift的过滤器方法来分离作为拐点的所需约束。还有更复杂的,但滤镜做得很好。
使用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!
}()
....
一段时间后…
@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
}
}
}
许多错误的转弯
这些笔记实际上是我为自己写的一套技巧。我亲自痛苦地做了所有不该做的事。希望这篇指南可以帮助其他人。
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).
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.
Play around with the values in the animation and consider using
other animateWithDuration variants.
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. CGRectContainsRect is one
example
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
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
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)
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)
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.
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:
使用故事板时要避免的解决方案的快速示例
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];
为了更好地了解视觉效果,可以看看苹果公司的这段早期视频。
密切关注
通常在文档中,会有一些小的注释或代码片段导致更大的想法。例如,将自动布局约束附加到动态动画器是一个很好的想法。
祝你好运,愿原力与你同在。