我使用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开发作为一种业余爱好。
答案 0 :(得分:1)
动画仍然有点不稳定,虽然接近我想要的。
鉴于此,似乎Core Animation实际上每帧都在绘制你的图层(或者尝试,无论如何)。
不幸的是,一旦你对每个帧执行自定义图层绘制,你的性能就会变成主线程:通常,对于Core Animation本身可以动画的属性(例如边界),动画本身会在渲染服务器中呈现,从应用程序进行进程外操作,并拥有自己的高优先级渲染线程。应用程序的主线程可以在这些动画类型中随意执行任何操作,而不会中断动画。
但是, drawInContext(_:)
在应用程序的主线程上调用。如果你在那里放一个日志语句或断点,它是否会在动画持续时间内多次调用?如果是这样,那么图层就是正确的动画。你在这个函数中的绘图操作可能是阻碍动画的。
尝试在图层上设置drawsAsynchronously
到true
。这将绘图命令推迟到后台线程,有时可以提高性能。 (请注意,大多数(如果不是全部)UIGraphics和Core Graphics功能在iOS 4中都是线程安全的,因此背景绘图是安全的。)
此外,根据动画的复杂程度,您可能需要提前绘制几个中间表示(如果可能,在背景中并行绘制)。如果它们不是太大,请将它们存储在内存中,以便您只需在图层中显示一些这些位图,而不是及时绘制它们。
答案 1 :(得分:-1)
我写了一个名为ConcentricProgressRingView的UIView
子类,它的功能与您尝试做的类似。
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并在其上设置了一些参数来调整动画行为。代码是开源的,所以请查看。希望这有帮助!