CABasicAnimation创建CALayer的空默认值副本

Ben*_*row 1 core-animation radial-gradients calayer cabasicanimation swift

我有一个自定义的CALayer,它绘制径向渐变。除动画期间外,它的效果都很好。似乎每次的迭代CABasicAnimation都会创建CALayer具有空默认属性值的子类的新副本:

调试类

在上面的屏幕截图中,您看到CABasicAnimation已创建了该图层的新副本并正在更新,gradientOrigin但没有其他属性可以进行。

结果是在动画期间不渲染任何内容。这是GIF:

不当

这是应该是什么样的:

正确

这是动画代码:

let animation = CABasicAnimation(keyPath: "gradientOrigin")
animation.duration = 2
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
let newOrigin: CGPoint = CGPoint(x: 0, y: triangle.bounds.height/2)
animation.fromValue = NSValue(CGPoint: triangle.gradientLayer.gradientOrigin)
animation.toValue = NSValue(CGPoint: newOrigin)
triangle.gradientLayer.gradientOrigin = newOrigin
triangle.gradientLayer.addAnimation(animation, forKey: nil)
Run Code Online (Sandbox Code Playgroud)

这是自定义CALayer代码:

enum RadialGradientLayerProperties: String {
  case gradientOrigin
  case gradientRadius
  case colors
  case locations
}

class RadialGradientLayer: CALayer {
  var gradientOrigin = CGPoint() {
    didSet { setNeedsDisplay() }
  }
  var gradientRadius = CGFloat() {
    didSet { setNeedsDisplay() }
  }

  var colors = [CGColor]() {
    didSet { setNeedsDisplay() }
  }

  var locations = [CGFloat]() {
    didSet { setNeedsDisplay() }
  }

  override init(){
    super.init()
    needsDisplayOnBoundsChange = true
  }

  required init(coder aDecoder: NSCoder) {
    super.init()
  }

  override init(layer: AnyObject) {
    super.init(layer: layer)
  }

  override class func needsDisplayForKey(key: String) -> Bool {
    if key == RadialGradientLayerProperties.gradientOrigin.rawValue || key == RadialGradientLayerProperties.gradientRadius.rawValue || key == RadialGradientLayerProperties.colors.rawValue || key == RadialGradientLayerProperties.locations.rawValue {
      print("called \(key)")
      return true
    }
    return super.needsDisplayForKey(key)

  }

  override func actionForKey(event: String) -> CAAction? {
    if event == RadialGradientLayerProperties.gradientOrigin.rawValue || event == RadialGradientLayerProperties.gradientRadius.rawValue || event == RadialGradientLayerProperties.colors.rawValue || event == RadialGradientLayerProperties.locations.rawValue {
      let animation = CABasicAnimation(keyPath: event)
      animation.fromValue = self.presentationLayer()?.valueForKey(event)
      return animation
    }
    return super.actionForKey(event)
  }

  override func drawInContext(ctx: CGContext) {

    guard let colorRef = self.colors.first else { return }

    let numberOfComponents = CGColorGetNumberOfComponents(colorRef)
    let colorSpace = CGColorGetColorSpace(colorRef)

    let deepGradientComponents: [[CGFloat]] = (self.colors.map {
      let colorComponents = CGColorGetComponents($0)
      let buffer = UnsafeBufferPointer(start: colorComponents, count: numberOfComponents)
      return Array(buffer) as [CGFloat]
      })

    let flattenedGradientComponents = deepGradientComponents.flatMap({ $0 })

    let gradient = CGGradientCreateWithColorComponents(colorSpace, flattenedGradientComponents, self.locations, self.locations.count)

    CGContextDrawRadialGradient(ctx, gradient, self.gradientOrigin, 0, self.gradientOrigin, self.gradientRadius, .DrawsAfterEndLocation)

  }
}
Run Code Online (Sandbox Code Playgroud)

Ben*_*row 7

找出答案!

在其中,init(layer:)您必须手动将属性值复制到您的类中。这是实际的效果:

override init(layer: AnyObject) {
  if let layer = layer as? RadialGradientLayer {
    gradientOrigin = layer.gradientOrigin
    gradientRadius = layer.gradientRadius
    colors = layer.colors
    locations = layer.locations
  }
  super.init(layer: layer)
}
Run Code Online (Sandbox Code Playgroud)

  • 在文档中:“此初始化程序用于为presentationLayer方法创建图层的卷影副本。在任何其他情况下使用此方法都会产生未定义的行为。例如,请勿使用此方法来初始化具有以下内容的新图层现有层的内容。如果要实现自定义层子类,则可以重写此方法并将其用于将实例变量的值复制到新对象中。子类应始终调用超类实现。” (2认同)