使用 Swift3 子类化 CALayer + 隐式动画

Subclassing CALayer + Implicit Animation w/ Swift3

我正在尝试通过覆盖 CALayer 来实现 "implicit animations"。我通读了文档,发现它相当复杂。此外,我找不到任何像样的代码示例,也找不到很好地解释它或使用任何 Swift 的互联网帖子。

我已经通过子类 BandBaseCG 实现,它试图为 属性 param1 提供隐式动画,并覆盖了 needsDisplay(forKey:),它确实被我的 属性 其中我 return 正确。

但是这里出了问题。我的理解是我必须 return 一个符合 CAAction 协议的对象,比如 CABasicAnimation,这就是我所做的。但是,action(forKey:)defaultAction(forKey:) 不会使用 param1 键调用。

所以我知道在某些时候我可以在 draw(in ctx:) 中提供 Core-Graphics 绘图代码来实现实际的隐式动画以响应某些 layer.param1 = 2。但我无法达到这一点,因为 API 根本没有按照我的预期工作。

这是我当前的实现:

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

}

这是一个完整的工作示例(来自我的书):

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

你会发现,如果你把一个 MyView 放到你的应用中,你可以说

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

并且它将动画化边框粗细的变化。