我已经为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.
}
}
答案 0 :(得分:0)
我的解决方案是在Objective-c中,你需要采用swift。试一试: -
创建NSOperationQueue并将动画调用添加到此队列。设置setMaxConcurrentOperationCount = 1
以确保顺序执行。
NSOperationQueue *animQueue = [[NSOperationQueue alloc] init];
[animQueue setMaxConcurrentOperationCount:1];
现在从函数actionForKey
而不是调用动画函数,将其添加到操作队列
NSBlockOperation *animOperation = [[NSBlockOperation alloc] init];
__weak NSBlockOperation *weakOp = animOperation;
[weakOp addExecutionBlock:^{
[self makeAnimationForKey:event)]
}];
[animQueue addOperation:animOperation];
我没有对此进行测试,但我认为它应该可行。
答案 1 :(得分:0)
试试这个..
UIview.animateWithDuration(2.0, delay 2.0, options: .CurveEaseIn, animations: {
//enter item to be animated here
}, completion: nil)
答案 2 :(得分:0)
如果要将两个动画链接在一起,以便在另一个动画完成时启动,请不要使用动画通知。而是使用动画对象的beginTime属性在所需的时间启动每个属性。要将两个动画链接在一起,请将第二个动画的开始时间设置为第一个动画的结束时间。有关动画和计时值的更多信息,请参阅自定义动画的计时。
我有一些工作,但我发现它不是最理想的解决方案。我宁愿认为通知是一种更好的方式,但是,如果Apple说的那么正确......
我还必须设置:animation.fillMode = kCAFillModeBackwards 否则,模型图层会使圆圈在动画制作之前达到最终状态,之后才会触发动画。
修改强>
好的,所以我发现为我的动画使用多个图层的速度很慢(我为每个视图使用了多个图层,所以我想要制作一个圆形动画,我有大约六个'em。那么8次非常慢(6x8) = 48动画)。)。在一层中制作动画片的速度要快得多。