如何使用自定义动画选择性地为 CALayer 设置 属性 动画

How to optionally animate property of a CALayer with custom animation

我有一个带有自定义动画的 CALayer,通过 @NSManaged 属性 工作并覆盖:

但是,有时我想绕过动画并让 属性 立即进入新值。在我的 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(相对于 truenil)——如果是,它 returns nil 阻止动画。