使用 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
并且它将动画化边框粗细的变化。
我正在尝试通过覆盖 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
并且它将动画化边框粗细的变化。