繁体   English   中英

Swift:从UIView按顺序对图层进行动画处理

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM