如何使用自定义动画选择性地为 CALayer 设置 属性 动画
How to optionally animate property of a CALayer with custom animation
我有一个带有自定义动画的 CALayer,通过 @NSManaged
属性 工作并覆盖:
class func defaultValue(forKey key: String) -> Any?
class func needsDisplay(forKey key: String) -> Bool
func action(forKey key: String) -> CAAction?
func display()
但是,有时我想绕过动画并让 属性 立即进入新值。在我的 CALayer
子 class 中,我试过这个:
@NSManaged private var radius: CGFloat
func animate(to radius: CGFloat) {
self.radius = radius
}
func step(to radius: CGFloat) {
// Inspired by
CATransaction.begin()
CATransaction.setDisableActions(true) // Prevents animation occurring
self.radius = radius
CATransaction.commit()
setNeedsDisplay() // Has no effect
}
当 animate(to:)
被调用时,display()
被重复调用,我的自定义绘图代码可以做到这一点。当调用 step(to:)
时,CATransaction
代码确实会阻止动画的发生,但根本不会执行绘图。
我可以让它按预期运行,但感觉很老套:
func step(to radius: CGFloat) {
// func action(forKey key: String) -> CAAction? uses .animationDuration
// when constructing a CABasicAnimation
let duration = animationDuration
defer { animationDuration = duration }
animationDuration = 0
self.radius = radius
}
让调用者能够选择 属性 是从一个值动画到下一个值,还是立即执行的正确方法是什么?对 radius
的后续更改应尊重先前的值,无论是步进还是动画。
您说您已经实现了 action(forKey:)
(非常正确)。所以在那些你不希望这个 属性 被动画化的场合,return nil
来自那个方法。绘图仍会进行,但没有动画。
或者,您可以 return super.action(forKey:key)
。这样可能更理智一点,但结果是一样的。
您可能会问(我希望您会问):我怎样才能抛出某种开关,action(forKey:)
可以查阅以便知道这是哪种场合?一种可能性是使用键值编码设置层的属性。
CALayer 有一个很棒的功能,你可以为任何键调用 setValue(_:forKey:)
或 value(forKey:)
;它不必是已经存在的“真实”密钥。
所以你可以在设置属性之前在图层上调用setValue(false, forKey:"shouldAnimate")
。然后你的 action(forKey:)
可以参考 value(forKey:"shouldAnimate")
,看看它是否是 false
(相对于 true
或 nil
)——如果是,它 returns nil
阻止动画。
我有一个带有自定义动画的 CALayer,通过 @NSManaged
属性 工作并覆盖:
class func defaultValue(forKey key: String) -> Any?
class func needsDisplay(forKey key: String) -> Bool
func action(forKey key: String) -> CAAction?
func display()
但是,有时我想绕过动画并让 属性 立即进入新值。在我的 CALayer
子 class 中,我试过这个:
@NSManaged private var radius: CGFloat
func animate(to radius: CGFloat) {
self.radius = radius
}
func step(to radius: CGFloat) {
// Inspired by
CATransaction.begin()
CATransaction.setDisableActions(true) // Prevents animation occurring
self.radius = radius
CATransaction.commit()
setNeedsDisplay() // Has no effect
}
当 animate(to:)
被调用时,display()
被重复调用,我的自定义绘图代码可以做到这一点。当调用 step(to:)
时,CATransaction
代码确实会阻止动画的发生,但根本不会执行绘图。
我可以让它按预期运行,但感觉很老套:
func step(to radius: CGFloat) {
// func action(forKey key: String) -> CAAction? uses .animationDuration
// when constructing a CABasicAnimation
let duration = animationDuration
defer { animationDuration = duration }
animationDuration = 0
self.radius = radius
}
让调用者能够选择 属性 是从一个值动画到下一个值,还是立即执行的正确方法是什么?对 radius
的后续更改应尊重先前的值,无论是步进还是动画。
您说您已经实现了 action(forKey:)
(非常正确)。所以在那些你不希望这个 属性 被动画化的场合,return nil
来自那个方法。绘图仍会进行,但没有动画。
或者,您可以 return super.action(forKey:key)
。这样可能更理智一点,但结果是一样的。
您可能会问(我希望您会问):我怎样才能抛出某种开关,action(forKey:)
可以查阅以便知道这是哪种场合?一种可能性是使用键值编码设置层的属性。
CALayer 有一个很棒的功能,你可以为任何键调用 setValue(_:forKey:)
或 value(forKey:)
;它不必是已经存在的“真实”密钥。
所以你可以在设置属性之前在图层上调用setValue(false, forKey:"shouldAnimate")
。然后你的 action(forKey:)
可以参考 value(forKey:"shouldAnimate")
,看看它是否是 false
(相对于 true
或 nil
)——如果是,它 returns nil
阻止动画。