Swift中自定义属性的隐式CALayer动画

时间:2015-10-28 16:49:06

标签: ios swift animation core-graphics calayer

我使用PaintCode创建了一个可动画的Core Graphics绘图。它基本上是一个圆形表(与Apple Watch戒指不同),它基本上会在计时器倒计时时填满。 meterLevel控制圆形仪表的填充水平,从0到100.基本上,如果计时器设置为10秒,我将meterLevel每1秒设置为90,80,70等......

这很好用,但动画只画1秒钟,看起来很不稳定。相反,我希望它是一个平滑的连续灌装仪表。

环顾四周似乎是继承CALayer并为meterLevel属性创建隐式动画可能就是这样。所以这就是我所拥有的:

import UIKit

class MeterControlView: UIView
{
var meterLevel: Int = 0 {
    didSet {
        self.layer.setValue(meterLevel, forKey: "meterLevel")
    }
}
var meterText: String = "00:00:00" {
    didSet {
        self.layer.setValue(meterText, forKey: "meterText")
    }
}


override class func layerClass() -> AnyClass {
    return MeterControlLayer.self
}

override func drawRect(rect: CGRect) {
    // Do nothing
    }
}

class MeterControlLayer: CALayer
{
@NSManaged
var meterLevel: Int
var meterText: String = "00:00:00"

override class func needsDisplayForKey(key: String) -> Bool {
    if (key == "meterLevel") {
        return true
    }
    return super.needsDisplayForKey(key)
}

override func actionForKey(key: String) -> CAAction? {
    if (key == "meterLevel") {
        let anim: CABasicAnimation = CABasicAnimation.init(keyPath: key)
        anim.fromValue = self.presentationLayer()?.meterLevel
        anim.duration = 1.0
        return anim
    } else {
        return super.actionForKey(key)
    }
}

override func drawInContext(ctx: CGContext) {
    super.drawInContext(ctx)
    UIGraphicsPushContext(ctx)
    XntervalStyleKit.drawMeterControl(frame: self.bounds, meterTime: meterText, meterLevelValue: CGFloat(meterLevel))
    UIGraphicsPopContext()
    }
}

遗憾的是,这并不像我期望的那样完美。动画仍然有点不稳定,虽然更接近我想要的。

我的问题更为笼统,这是完成我想做的事情的正确方法吗?我无法找到设置图层属性meterLevel和meterText的正确方法,而不使用setValueForKey:。这是正确的方法吗?

动画/图形对我来说绝对是新手。我是一个嵌入式C软件,试图将iOS开发作为一种业余爱好。

2 个答案:

答案 0 :(得分:1)

  

动画仍然有点不稳定,虽然接近我想要的。

鉴于此,似乎Core Animation实际上每帧都在绘制你的图层(或者尝试,无论如何)。

不幸的是,一旦你对每个帧执行自定义图层绘制,你的性能就会变成主线程:通常,对于Core Animation本身可以动画的属性(例如边界),动画本身会在渲染服务器中呈现,从应用程序进行进程外操作,并拥有自己的高优先级渲染线程。应用程序的主线程可以在这些动画类型中随意执行任何操作,而不会中断动画。

但是,

drawInContext(_:)在应用程序的主线程上调用。如果你在那里放一个日志语句或断点,它是否会在动画持续时间内多次调用?如果是这样,那么图层就是正确的动画。你在这个函数中的绘图操作可能是阻碍动画的。

尝试在图层上设置drawsAsynchronouslytrue。这将绘图命令推迟到后台线程,有时可以提高性能。 (请注意,大多数(如果不是全部)UIGraphics和Core Graphics功能在iOS 4中都是线程安全的,因此背景绘图是安全的。)

此外,根据动画的复杂程度,您可能需要提前绘制几个中间表示(如果可能,在背景中并行绘制)。如果它们不是太大,请将它们存储在内存中,以便您只需在图层中显示一些这些位图,而不是及时绘制它们。

答案 1 :(得分:-1)

我写了一个名为ConcentricProgressRingViewUIView子类,它的功能与您尝试做的类似。

https://github.com/lionheart/ConcentricProgressRingView

以下是一个示例:

用法

UIViewController的顶部,导入模块:

import ConcentricProgressRingView

然后,在viewDidLoad

let rings = [
    ProgressRing(color: UIColor(.RGB(232, 11, 45)), backgroundColor: UIColor(.RGB(34, 3, 11))),
    ProgressRing(color: UIColor(.RGB(137, 242, 0)), backgroundColor: UIColor(.RGB(22, 33, 0))),
    ProgressRing(color: UIColor(.RGB(0, 200, 222)), backgroundColor: UIColor(.RGB(0, 30, 28)))
]
let progressRingView = try! ConcentricProgressRingView(center: view.center, radius: radius, margin: margin, rings: rings, defaultColor: UIColor.clearColor(), defaultWidth: 18)
view.addSubview(progressRingView)

在您实例化ConcentricProgressRingView个实例后,使用setProgress将特定铃声设为百分比。

ring.arcs[1].setProgress(0.5, duration: 2)

如何运作

我不完全确定我在问题中与您的示例代码做了哪些不同,但我创建了一个CABasicAnimation并在其上设置了一些参数来调整动画行为。代码是开源的,所以请查看。希望这有帮助!