使用 Swift3 子类化 CALayer + 隐式动画
Posted
技术标签:
【中文标题】使用 Swift3 子类化 CALayer + 隐式动画【英文标题】:Subclassing CALayer + Implicit Animation w/ Swift3 【发布时间】:2016-11-24 23:36:09 【问题描述】:我正在尝试通过覆盖 CALayer 来实现“隐式动画”。我已经阅读了文档,发现它相当复杂。此外,我找不到任何像样的代码示例,也找不到很好地解释它或使用任何 Swift 的互联网帖子。
我已经通过子类BandBaseCG
实现,它试图为属性param1
提供隐式动画,并覆盖needsDisplay(forKey:)
,它在我的属性中调用一次,我返回true。
但这就是问题所在。我的理解是我必须返回一个符合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)")
【问题讨论】:
【参考方案1】:这是一个完整的工作示例(来自我的书中):
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)
你会发现,如果你在你的app里放了一个MyView,你可以说
let lay = v.layer as! MyLayer
lay.thickness = 10 // or whatever
并且它将动画边框厚度的变化。
【讨论】:
“此外,我找不到任何像样的代码示例,也找不到很好地解释它或使用任何 Swift 的互联网帖子。”好的,我认为这是一个“体面”的代码示例。至于解释,你显然没有读过我的书,这不是你的错。但它确实存在,我认为它很好地解释了该代码的所有部分(当然只是我的看法)。 我认为这不公平。我花了几个小时在互联网上搜索,发现关于这个问题的信息很少。至于你的书,我很高兴知道它,将来一定会查阅它。感谢您的回复。 放松。我说这不是你的错!无害无犯。我已经给了你代码,你会发现它可以工作(后面有一个可下载的完整项目)。我们在这里是同一个团队。 对不起,我没听懂“语气”。你的权利,这是一个很好的例子,我很感激。具体来说,似乎#keypath(我在阅读的任何教程中都没有提到,并且是一个有点晦涩的语言功能)是我需要的关键功能差异,是吗? @C0D3 你必须想想 Cocoa / Objective-C 在幕后做了什么。 Swift 只是一种与之交谈的方式。所以,Swift@NSManaged
是 Objective-C dynamic
,这意味着 setter/getter 将在运行时合成,这就是我们在这里需要的,就像在 Core Data 中一样。请参阅apeth.com/iosBook/ch17.html#_making_a_property_animatable,了解在此处“翻译”为 Swift 的底层 Objective-C 机制。 (Swift dynamic
完全是另外一回事。)以上是关于使用 Swift3 子类化 CALayer + 隐式动画的主要内容,如果未能解决你的问题,请参考以下文章
通过 AVExportSession 导出带有隐式动画的 CALayer