0

Ich habe einen benutzerdefinierten CALayer, der radiale Farbverläufe zeichnet. Es funktioniert gut, außer während der Animation. Es scheint, dass jede Iteration von CABasicAnimation eine neue Kopie der CALayer Unterklasse mit leeren, Standardwerte für die Eigenschaften erzeugt: obenCABasicAnimation erstellt leere Standardwerte Kopie von CALayer

debug class

Im Screenshot sehen Sie, dass CABasicAnimation eine neue Kopie der Ebene geschaffen und aktualisiert gradientOrigin, aber keine der anderen Eigenschaften sind für die Fahrt gekommen.

Dies hat zur Folge, dass während der Animation nichts gerendert wird. Hier ist ein GIF:

improper

Hier ist, was aussehen soll ist:

correct

Hier ist die Animation Code:

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) 

Hier ist der Brauch CALayer Code:

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) 

    } 
} 

Antwort

2

Die Antwort gefunden! In init(layer:) müssen Sie die Eigenschaftswerte manuell in Ihre Klasse kopieren. Hier ist, wie das in Aktion aussieht:

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

Aus der Dokumentation: „Das initializer wird verwendet, Schattenkopien von Schichten zu erzeugen, zum Beispiel für die presentationLayer Methode Mit dieser Methode in jeder anderen Situation wird nicht definiertes Verhalten erzeugt Für.. Beispiel: Verwenden Sie diese Methode nicht, um eine neue Ebene mit dem Inhalt einer vorhandenen Ebene zu initialisieren.Wenn Sie eine benutzerdefinierte Ebenenunterklasse implementieren, können Sie diese Methode überschreiben und sie verwenden, um die Werte von Instanzvariablen in das neue Objekt zu kopieren Rufen Sie die Superklassenimplementierung auf. " – matt

Verwandte Themen