[英]Swift: Animate layers in sequence from UIView
我在UIView中添加了几层,现在希望对它们进行动画处理。
我有5层动画,这些都是圆圈。 我使用可观察的字段来通知值的变化,并开始使用needsDisplayForKey()和actionForKey()方法进行动画处理。
可观察的字段:
@NSManaged private var _startAngle: CGFloat
@NSManaged private var _endAngle: CGFloat
当前,当我更改每一层的可观察字段之一时,所有层都同时设置动画。 我希望对此进行更改,并希望在2.5秒的时间范围内依次对所有这些对象进行动画处理。 因此,当一个动画结束时,我希望为下一层动画等。
现在,我唯一可以知道动画完成的方法是使用animationDidStop()方法。 但是,以这种方式,我无法知道正在制作动画的其他图层。 你们知道这将是一个好的解决方案吗?
我的图层:
class HourLayer: CAShapeLayer {
@NSManaged private var _startAngle: CGFloat
@NSManaged private var _endAngle: CGFloat
@NSManaged private var _fillColor: UIColor
@NSManaged private var _strokeWidth: CGFloat
@NSManaged private var _strokeColor: UIColor
@NSManaged private var _duration: CFTimeInterval
private var _previousEndAngle: CGFloat = 0.0
private var _previousStartAngle: CGFloat = 0.0
private let _lenghtOfArc: CGFloat = CGFloat(2 * M_PI)
internal var StartAngle: CGFloat {
get {
return _startAngle
}
set {
_startAngle = newValue
}
}
internal var EndAngle: CGFloat {
get {
return _endAngle
}
set {
_endAngle = newValue
}
}
internal var FillColor: UIColor {
get {
return _fillColor
}
set {
_fillColor = newValue
}
}
internal var StrokeWidth: CGFloat {
get {
return _strokeWidth
}
set {
_strokeWidth = newValue
}
}
internal var StrokeColor: UIColor {
get {
return _strokeColor
}
set {
_strokeColor = newValue
}
}
internal var DurationOfAnimation: CFTimeInterval {
get {
return _duration
}
set {
_duration = newValue
}
}
required override init!() {
super.init()
self._startAngle = 0.0
self._endAngle = 0.0
self._fillColor = UIColor.clearColor()
self._strokeWidth = 4.0
self._strokeColor = UIColor.blackColor()
self._duration = 1.0
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
fatalError("required init(:coder) is missing")
}
override init!(layer: AnyObject!) {
super.init(layer: layer)
if layer.isKindOfClass(HourLayer) {
var other: HourLayer = layer as HourLayer
self._startAngle = other._startAngle
self._endAngle = other._endAngle
self._fillColor = other._fillColor
self._strokeWidth = other._strokeWidth
self._strokeColor = other._strokeColor
self._duration = other._duration
}
}
override var frame: CGRect {
didSet {
self.setNeedsLayout()
self.setNeedsDisplay()
}
}
override func actionForKey(event: String!) -> CAAction! {
if event == "_startAngle" || event == "_endAngle" {
return makeAnimationForKey(event)
}
return super.actionForKey(event)
}
private func makeAnimationForKey(event: String) -> CABasicAnimation {
// We want to animate the strokeEnd property of the circleLayer
let animation: CABasicAnimation = CABasicAnimation(keyPath: event)
// Set the animation duration appropriately
animation.duration = self._duration
// When no animation has been triggered and the presentation layer does not exist yet.
if self.presentationLayer() == nil {
if event == "_startAngle" {
animation.fromValue = self._previousStartAngle
self._previousStartAngle = self._startAngle
} else if event == "_endAngle" {
animation.fromValue = self._previousEndAngle
self._previousEndAngle = self._endAngle
}
} else {
animation.fromValue = self.presentationLayer()!.valueForKey(event)
}
animation.removedOnCompletion = false
animation.delegate = self
// Do a linear animation (i.e. the speed of the animation stays the same)
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
return animation
}
override class func needsDisplayForKey(key: String) -> Bool {
if key == "_startAngle" || key == "_endAngle" {
return true
}
return super.needsDisplayForKey(key)
}
override func drawInContext(ctx: CGContext!) {
var center: CGPoint = CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2)
var radius: CGFloat = CGFloat((CGFloat(self.bounds.width) - CGFloat(_strokeWidth)) / 2)
// Set the stroke color
CGContextSetStrokeColorWithColor(ctx, _strokeColor.CGColor)
// Set the line width
CGContextSetLineWidth(ctx, _strokeWidth)
// Set the fill color (if you are filling the circle)
CGContextSetFillColorWithColor(ctx, UIColor.clearColor().CGColor)
// Draw the arc around the circle
CGContextAddArc(ctx, center.x, center.y, radius, _startAngle, _lenghtOfArc * _endAngle, 0)
// Draw the arc
CGContextDrawPath(ctx, kCGPathStroke) // or kCGPathFillStroke to fill and stroke the circle
super.drawInContext(ctx)
}
override func animationDidStop(anim: CAAnimation!, finished flag: Bool) {
// Trigger animation for other layer by setting their values for _startAngle and _endAngle.
}
}
我的解决方案是在Objective-C中,您需要快速采用它。 试试看: -
创建一个NSOperationQueue并将动画调用添加到此队列。 设置setMaxConcurrentOperationCount = 1
以确保顺序执行。
NSOperationQueue *animQueue = [[NSOperationQueue alloc] init];
[animQueue setMaxConcurrentOperationCount:1];
现在,从actionForKey
函数而不是调用animation函数,将其添加到操作队列中
NSBlockOperation *animOperation = [[NSBlockOperation alloc] init];
__weak NSBlockOperation *weakOp = animOperation;
[weakOp addExecutionBlock:^{
[self makeAnimationForKey:event)]
}];
[animQueue addOperation:animOperation];
我没有测试过,但是我认为它应该工作。
尝试这个..
UIview.animateWithDuration(2.0, delay 2.0, options: .CurveEaseIn, animations: {
//enter item to be animated here
}, completion: nil)
Apple文档规定了以下内容: https : //developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreAnimation_guide/CreatingBasicAnimations/CreatingBasicAnimations.html
如果要将两个动画链接在一起,以便一个动画在另一个动画结束时开始,请不要使用动画通知。 而是使用动画对象的beginTime属性在所需的时间开始每个动画对象。 要将两个动画链接在一起,请将第二个动画的开始时间设置为第一个动画的结束时间。 有关动画和定时值的更多信息,请参见自定义动画的定时。
我有一些有用的东西,但是我发现它不是最理想的解决方案。 我宁愿认为通知是一种更好的方法,但是如果Apple说的没错,那就嘿。
我还必须设置:animation.fillMode = kCAFillModeBackwards否则,模型层将使圆在动画前达到其最终状态,并且仅在以后触发动画。
编辑
好的,所以我发现为动画使用多层很慢(我为每个视图使用了多层,所以我想制作一个动画圈,并且有大约六个'em。这样做8的速度非常慢(6x8 = 48个动画) )。)。 将所有内容动画化为一层的方法更快。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.