Animaciones de CALayer implícitas para propiedad personalizada en Swift

He creado un dibujo de Core Graphics animatable usando PaintCode. Es básicamente un medidor de círculo (no muy diferente de los anillos de Apple Watch), que básicamente se llena cuando el timer count hacia abajo. El levelLevel controla el nivel de llenado del medidor de círculo, de 0 a 100. Básicamente, si un timer se establece en 10 segundos, ajuste el nivel de medidor cada 1 segundo a 90, 80, 70, etc.

Esto funciona bien, sin embargo, la animation solo se dibuja 1 segundo y se ve bastante entrecortada. En cambio, me gustaría que fuera un medidor de llenado continuo y liso.

Mirando a su alnetworkingedor parecía ser una subclass de CALayer y crear una animation implícita para la propiedad meterLevel podría ser el path a seguir. Entonces esto es lo que tengo:

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() } } 

Desafortunadamente, esto no funciona exactamente como esperaba. La animation sigue siendo un poco entrecortada, aunque más cerca de lo que quiero.

Sin embargo, mi pregunta es más general, ¿es este el path correcto para lograr lo que quiero hacer? No pude descubrir la forma correcta de establecer las properties de la capa meterLevel y meterText, sin usar setValueForKey :. ¿Es esa la manera correcta de hacer esto?

Animación / Gráficos es definitivamente nuevo para mí. Soy un tipo de software embedded de C que intenta incorporarse al desarrollo de iOS como un pasatime.

La animation sigue siendo un poco entrecortada, aunque más cerca de lo que quiero.

Dado esto, parece como si Core Animation estuviera realmente dibujando tu capa en cada fotogtwig (o intentando, de todos modos).

Desafortunadamente, una vez que realiza la capa personalizada dibujando cada fotogtwig, su performance se convierte en hilo principal: normalmente, para las properties que Core Animation puede animar de forma nativa (como los límites), la animation en sí misma se representa en el server de renderizado, que funciona fuera de process desde su aplicación y tiene su propio hilo de renderizado de alta prioridad. El hilo principal de su aplicación es libre de hacer lo que quiera durante estos types de animation sin ninguna interrupción de la animation.

drawInContext(_:) , sin embargo, se drawInContext(_:) en el hilo principal de su aplicación. Si pones una statement de logging o punto de interrupción allí, ¿se llama muchas veces en el transcurso de tu duración de animation? Si es así, entonces la capa está animando correctamente. Las operaciones de dibujo dentro de esta function pueden ser lo que mantiene la animation.

Intente establecer drawsAsynchronously a true en su capa. Esto difiere los commands de dibujo en un hilo de background, y puede mejorar el performance a veces. (Tenga en count que la mayoría, si no todos, las funciones UIGraphics y Core Graphics son seguras para subprocesss a partir de iOS 4, por lo que el dibujo en segundo plano es seguro).

Además, dependiendo de lo compleja que sea su animation, es posible que desee dibujar varias representaciones intermedias por adelantado (en el background y en paralelo, si es posible). Guarde estos en algún lugar de la memory si no son demasiado grandes, por lo que puede simplemente mostrar algunos de estos maps de bits en su capa en lugar de dibujarlos justo a time.

Escribí una subclass UIView llamada ConcentricProgressRingView que hace algo similar a lo que intentas hacer.

https://github.com/lionheart/ConcentricProgressRingView

Aquí hay un ejemplo de cómo se ve:

Uso

En la parte superior de su UIViewController , importe el module:

 import ConcentricProgressRingView 

Entonces, en su vista, 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) 

Una vez que hayas instanciado tu instancia de ConcentricProgressRingView , anima un anillo específico a un porcentaje con setProgress .

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

Cómo funciona

No estoy exactamente seguro de qué estoy haciendo de manera diferente a su código de ejemplo en la pregunta, pero estoy creando una CABasicAnimation y configurando algunos parameters para modificar el comportamiento de la animation. El código es de código abierto, así que échale un vistazo. ¡Espero que esto ayude!