Swift:从UIView依次为图层添加动画

时间:2015-03-13 08:55:01

标签: ios swift animation cashapelayer

我已经为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.
}
}

3 个答案:

答案 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)

Apple文档说明了以下内容:https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreAnimation_guide/CreatingBasicAnimations/CreatingBasicAnimations.html

  

如果要将两个动画链接在一起,以便在另一个动画完成时启动,请不要使用动画通知。而是使用动画对象的beginTime属性在所需的时间启动每个属性。要将两个动画链接在一起,请将第二个动画的开始时间设置为第一个动画的结束时间。有关动画和计时值的更多信息,请参阅自定义动画的计时。

我有一些工作,但我发现它不是最理想的解决方案。我宁愿认为通知是一种更好的方式,但是,如果Apple说的那么正确......

我还必须设置:animation.fillMode = kCAFillModeBackwards 否则,模型图层会使圆圈在动画制作之前达到最终状态,之后才会触发动画。

修改

好的,所以我发现为我的动画使用多个图层的速度很慢(我为每个视图使用了多个图层,所以我想要制作一个圆形动画,我有大约六个'em。那么8次非常慢(6x8) = 48动画)。)。在一层中制作动画片的速度要快得多。