斯威夫特:从UIView的动画层序列 [英] Swift: Animate layers in sequence from UIView

查看:221
本文介绍了斯威夫特:从UIView的动画层序列的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经添加了几层到UIView的,现在想按顺序这些动画

我有5层的动画,这些都是各界。我用观察的领域得到通知中的值的变化,并开始使用needsDisplayForKey()和actionForKey()方法来制作动画。

可观察到的字段:

  @NSManaged私人VAR _startAngle:CGFloat的@NSManaged私人VAR _endAngle:CGFloat的

目前所有的层动画在同一时间,当我改变每个层观察的领域之一。我想改变这一点,希望按顺序以动画所有的人都在2.5秒的时间跨度。所以,当一个动画完成后,我希望动画下一层等。

现在我可以知道,动画已完成的唯一方法是通过使用animationDidStop()方法。不过这样一来我不知道是动画其他层的方式。难道你们知道一个很好的解决办法是什么呢?

我的层:

 类HourLayer:CAShapeLayer {@NSManaged私人VAR _startAngle:CGFloat的@NSManaged私人VAR _endAngle:CGFloat的@NSManaged私人VAR _fillColor这样的:UIColor@NSManaged私人VAR _strokeWidth:CGFloat的@NSManaged私人VAR _strokeColor这样的:UIColor@NSManaged私人VAR _duration:CFTimeInterval私人变种_ previousEndAngle:CGFloat的= 0.0
私人变种_ previousStartAngle:CGFloat的= 0.0私人让_lenghtOfArc:CGFloat的= CGFloat的(2 * M_PI)内部VAR由startAngle:CGFloat的{
    获得{
        返回_startAngle
    }
    集合{
        _startAngle =为newValue
    }
}内部VAR EndAngle:CGFloat的{
    获得{
        返回_endAngle
    }
    集合{
        _endAngle =为newValue
    }
}内部VAR填充颜色:{的UIColor
    获得{
        返回_fillColor
    }
    集合{
        _fillColor =为newValue
    }
}内部VAR StrokeWidth:CGFloat的{
    获得{
        返回_strokeWidth
    }
    集合{
        _strokeWidth =为newValue
    }
}内部VAR则strokeColor:{的UIColor
    获得{
        返回_strokeColor
    }
    集合{
        _strokeColor =为newValue
    }
}内部VAR DurationOfAnimation:CFTimeInterval {
    获得{
        回报_duration
    }
    集合{
        _duration =为newValue
    }
}需要重写的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
}所需的init(codeR ADE codeR:NS codeR){
    super.init(codeR:ADE codeR)
    包括fatalError(所需的init(:codeR)丢失)
}覆盖的init!(层:AnyObject!){
    super.init(层:一层)    如果layer.isKindOfClass(HourLayer){
        VAR其他:HourLayer =层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
    }
}覆盖VAR框架:{的CGRect
    didSet {
        self.setNeedsLayout()
        self.setNeedsDisplay()
    }
}覆盖FUNC actionForKey: - >(事件串!) CAAction! {
    如果事件==_startAngle||事件==_endAngle{
        返回makeAnimationForKey(事件)
    }
    返回super.actionForKey(事件)
}私人FUNC makeAnimationForKey(事件:字符串) - GT; CABasicAnimation {
    //我们希望动画circleLayer的strokeEnd财产
    让动画:CABasicAnimation = CABasicAnimation(的keyPath:事件)
    //正确设置动画持续时间
    animation.duration = self._duration    //当没有动画已触发和presentation层尚不存在。
    如果自我。presentationLayer()== {为零
        如果事件==_startAngle{
            animation.fromValue =自._ previousStartAngle
            自._ previousStartAngle = self._startAngle
        }否则,如果事件==_endAngle{
            animation.fromValue =自._ previousEndAngle
            自._ previousEndAngle = self._endAngle
        }
    }其他{
        animation.fromValue =自我。presentationLayer()!valueForKey(事件)
    }
    animation.removedOnCompletion = FALSE
    animation.delegate =自
    //执行线性动画(即,动画的速度保持相同)
    animation.timingFunction = CAMediaTimingFunction(名称:kCAMediaTimingFunctionEaseInEaseOut)
    返回动画
}覆盖类FUNC needsDisplayForKey(键:字符串) - GT;布尔{
    如果key ==_startAngle||关键==_endAngle{
        返回true
    }
    返回super.needsDisplayForKey(键)
}覆盖FUNC drawInContext(CTX:CGContext上){
    变种中心:CGPoint = CGPointMake(self.bounds.size.width / 2,self.bounds.size.height / 2)
    变种半径:CGFloat的= CGFloat的((CGFloat的(self.bounds.width) - CGFloat的(_strokeWidth))/ 2)    //设置笔触颜色
    CGContextSetStrokeColorWithColor(CTX,_strokeColor.CGColor)    //设置线宽
    CGContextSetLineWidth(CTX,_strokeWidth)    //设置填充颜色(如果你是填充圆)
    CGContextSetFillColorWithColor(CTX,UIColor.clearColor()。CGColor)    //绘制绕了一圈弧形
    CGContextAddArc(CTX,center.x,center.y,半径,_startAngle,_lenghtOfArc * _endAngle,0)    //画圆弧
    CGContextDrawPath(CTX,kCGPathStroke)//或kCGPathFillStroke填写和中风圆    super.drawInContext(CTX)
}覆盖FUNC animationDidStop(阿尼姆:CAAnimation !,完成标志:布尔){
    //由_startAngle和_endAngle设置它们的值触发其他层动画。
}
}


解决方案

苹果文档指出以下内容:<一href=\"https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreAnimation_guide/CreatingBasicAnimations/CreatingBasicAnimations.html\" rel=\"nofollow\">https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreAnimation_guide/CreatingBasicAnimations/CreatingBasicAnimations.html


  

如果你想链两个动画在一起,这样一个人开始当对方结束后,不要使用动画通知。相反,使用动画对象的属性BEGINTIME开始每一个在期望的时间。到链中的两个动画一起,设置第二动画到第一动画结束时间的开始时间。有关动画和时序值的更多信息,请参阅自定义动画的时间。


我已经得到了一些东西的工作,但我并不觉得最理想的解决方案。我宁愿认为,通知是一个更好的办法,但是,嘿,如果苹果公司说得太对了....

此外,我也必须设置:animation.fillMode = kCAFillModeBackwards
否则,模型层会使动画界之前达到他们的最终状态,后来才将动画触发。

修改

好了,所以我发现,使用多层我的动画是缓慢的(我用多层为每个视图,所以一个圆圈,我想动画,我有'时间约6。做那个时代8是非常慢(6×8 = 48动画)。)。这是公正的方式更快一层到动画的一切。

I've added a few layers to an UIView and now wish to animate these in sequence.

I have 5 layers to animate, these are all circles. I use observable fields to get notified of the changes in the values and start to animate using the needsDisplayForKey() and actionForKey() methods.

The observable fields:

@NSManaged private var _startAngle: CGFloat

@NSManaged private var _endAngle: CGFloat

Currently all the layers animate at the same time when I change one of the observable fields for each layer. I wish to change this and wish to animate all of them in sequence in a time span of 2.5 seconds. So when one animation has finished, I wish to animate the next layer etc.

For now the only way I can know that an animation has finished is by using the animationDidStop() method. However this way I have no way of knowing of the other layers that are animating. Do you guys know what a good solution would be for this?

My layer:

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.
}
}

解决方案

The Apple documentation states the following: https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreAnimation_guide/CreatingBasicAnimations/CreatingBasicAnimations.html

If you want to chain two animations together so that one starts when the other finishes, do not use animation notifications. Instead, use the beginTime property of your animation objects to start each one at the desired time. To chain two animations together, set the start time of the second animation to the end time of the first animation. For more information about animation and timing values, see Customizing the Timing of an Animation.

I have got some stuff working, but I do not find it the most ideal solution. I would rather think the notifications are a nicer way, but hey if Apple says so right....

Also I did have to set: animation.fillMode = kCAFillModeBackwards Otherwise the model layer would make the circles reach their final state before animating and only later would the animation trigger.

Edit

Ok so I found that using multiple layers for my animations is slow (I used multiple layers for each view, so a circle I wanted to animate and I had about six of 'em. Do that times 8 is very slow (6x8=48 animations).). It is just way faster to animate everything in one layer.

这篇关于斯威夫特:从UIView的动画层序列的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆