0

放射状のグラデーションを描画するカスタムCALayerがあります。アニメーション中以外は素晴らしい作品です。あなたはCABasicAnimationは、層の新しいコピーを作成していることがわかり、上のスクリーンショットでCABasicAnimationはCALayerの空のデフォルト値のコピーを作成します

debug class

CABasicAnimationの各反復は、新しいプロパティの空、デフォルト値でCALayerサブクラスのコピーを作成しているようですgradientOriginを更新していますが、その他のプロパティはありません。

これは、アニメーション中に何も描画しないという結果をもたらします。

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) 
ここ

カスタムCALayerのコードです:

ここcorrect

アニメーションコードです:

improper

ここのようになるはずです何:ここではGIFです

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) 

    } 
} 

答えて

2

答えを見つけました!

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) 
} 
+2

ドキュメントから:「このイニシャライザは、例えば、プレゼンテーション層方式のため、レイヤーのシャドウコピーを作成するために使用されている他の状況でこの方法を使用すると、未定義の振る舞いを生み出すために。たとえば、このメソッドを使用して既存のレイヤーのコンテンツで新しいレイヤーを初期化しないでください。カスタムレイヤーサブクラスを実装する場合、このメソッドをオーバーライドしてインスタンス変数の値を新しいオブジェクトにコピーできます。スーパークラスの実装を呼び出す。 – matt