Subclassing CALayer + Implicit Animation w/ Swift3

浪尽此生 提交于 2019-12-11 04:22:58

问题


I am trying to implement "implicit animations" by overriding CALayer. I've read through the documentation and find it rather complicated. More over, I can't find any decent code examples, or internet posts explaining it well or using any Swift.

I've implemented by subclass BandBaseCG which attempts to provide implicit animation for property param1, and overridden needsDisplay(forKey:) which does get called once with my property where I return true.

But here is where it goes wrong. My understanding is that I must return an object conforming to the CAAction protocol, say a CABasicAnimation, and this is what I do. However, action(forKey:) or defaultAction(forKey:) do not get called with the param1 key.

So I understand that at some point I can provide Core-Graphics drawing code in draw(in ctx:) to implement the actual implicit animation in response to some layer.param1 = 2. But I just cannot get to this point because the API is simply not working how I expected.

Here is my current implementation:

import Foundation
import QuartzCore

class BandBaseCG: CALayer {

   @NSManaged var param1: Float

   override init(layer: Any) {

      super.init(layer: layer)

   }

   override init() {
      super.init()

   }

   required init?(coder aDecoder: NSCoder) {
      fatalError("init(coder:) has not been implemented")
   }


   override func action(forKey event: String) -> CAAction? {

      print(event)

      if event == "param1" {

         let ba = CABasicAnimation(keyPath: event)
         ba.duration = 2
         ba.fromValue = (presentation()! as BandBaseCG).param1
         ba.toValue = 2

         return ba
      }

      return super.action(forKey: event)
   }


   override func draw(in ctx: CGContext) {
      print("draw")
   }


   override class func defaultAction(forKey event: String) -> CAAction?     {

      if event == "param1" {

         let ba = CABasicAnimation(keyPath: event)
         ba.duration = 2
         ba.fromValue = 0
         ba.toValue = 2

         return ba
      }

      return super.defaultAction(forKey: event)
   }

   override class func needsDisplay(forKey key: String) -> Bool {

      print(key)

      if key == "param1" {
         return true
      }

      return super.needsDisplay(forKey: key)
   }

   override func display() {
      print("param value: \(param1)")
   }

}

回答1:


Here's a complete working example (from my book):

class MyView : UIView { // exists purely to host MyLayer
    override class var layerClass : AnyClass {
        return MyLayer.self
    }
    override func draw(_ rect: CGRect) {} // so that layer will draw itself
}
class MyLayer : CALayer {
    @NSManaged var thickness : CGFloat
    override class func needsDisplay(forKey key: String) -> Bool {
        if key == #keyPath(thickness) {
            return true
        }
        return super.needsDisplay(forKey:key)
    }
    override func draw(in con: CGContext) {
        let r = self.bounds.insetBy(dx:20, dy:20)
        con.setFillColor(UIColor.red.cgColor)
        con.fill(r)
        con.setLineWidth(self.thickness)
        con.stroke(r)
    }
    override func action(forKey key: String) -> CAAction? {
        if key == #keyPath(thickness) {
            let ba = CABasicAnimation(keyPath: key)
            ba.fromValue = self.presentation()!.value(forKey:key)
            return ba
        }
        return super.action(forKey:key)
    }
}

You will find that if you put a MyView into your app, you can say

let lay = v.layer as! MyLayer
lay.thickness = 10 // or whatever

and that it will animate the change in the border thickness.



来源:https://stackoverflow.com/questions/40795806/subclassing-calayer-implicit-animation-w-swift3

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!