简体   繁体   English

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

[英]How to optionally animate property of a CALayer with custom animation

I have a CALayer with a custom animation on it, working via an @NSManaged property, and overriding:我有一个带有自定义 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()

However, I sometimes want to bypass the animation and have the property immediately step to the new value.但是,有时我想绕过 animation 并让属性立即进入新值。 In my CALayer sub-class I tried this:在我的CALayer子类中,我尝试了这个:

@NSManaged private var radius: CGFloat

func animate(to radius: CGFloat) {
    self.radius = radius
}

func step(to radius: CGFloat) {
    // Inspired by https://stackoverflow.com/a/34941743
    CATransaction.begin()
    CATransaction.setDisableActions(true)   // Prevents animation occurring
    self.radius = radius
    CATransaction.commit()
    setNeedsDisplay()                       // Has no effect
}

When animate(to:) is called, display() is called repeatedly and my custom drawing code can do it's thing.当调用animate(to:)时,会重复调用display()并且我的自定义绘图代码可以做到这一点。 When step(to:) is called, the CATransaction code does prevent an animation from occurring, but no drawing is ever performed at all.当调用step(to:)时, CATransaction代码确实阻止了 animation 的发生,但根本没有执行任何绘图。

I can get it to behave as desired, but it feels quite hacky:可以让它按预期运行,但感觉很老套:

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
}

What is the correct method to give the caller the ability to choose whether the property animates from one value to the next, or steps immediately?使调用者能够选择属性是从一个值到下一个值还是立即执行动画的正确方法是什么? A subsequent change to radius should respect the previous value, whether it was stepped or animated to.随后对radius的更改应遵循先前的值,无论是步进还是动画。

You say you have implemented action(forKey:) (quite rightly).你说你已经实施了action(forKey:) (非常正确)。 So on those occasions when you don't want this property to be animated, return nil from that method.所以在那些你不希望这个属性被动画化的情况下,从那个方法返回nil The drawing will still take place, but without animation.绘图仍将进行,但没有 animation。

Alternatively, you could return super.action(forKey:key) .或者,您可以返回super.action(forKey:key) That might be a little more sane, but the outcome is the same.这可能更理智一点,但结果是一样的。


You may ask (and I hope you do): How can I throw some kind of switch that action(forKey:) can consult in order to know which kind of occasion this is?你可能会问(我希望你会问):我怎样才能抛出某种开关,让action(forKey:)可以参考以了解这是哪种情况? One possibility is to set a property of the layer using key-value coding.一种可能性是使用键值编码设置层的属性。

CALayer has a wonderful feature that you are allowed to call setValue(_:forKey:) or value(forKey:) for any key; CALayer 有一个很棒的特性,你可以为任何键调用setValue(_:forKey:)value(forKey:) it doesn't have to be a "real" key that already exists.它不一定是已经存在的“真实”密钥。

So you could call setValue(false, forKey:"shouldAnimate") on the layer before setting the property.因此,您可以在设置属性之前在图层上调用setValue(false, forKey:"shouldAnimate") And your action(forKey:) can then consult value(forKey:"shouldAnimate") , and see whether it is false (as opposed to true or nil ) — and if it is, it returns nil to prevent the animation.然后你的action(forKey:)可以查询value(forKey:"shouldAnimate") ,看看它是否为false (相对于truenil )——如果是,它返回nil以防止 animation。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM