swiftcocoacabasicanimation

How can I sub class CABasicAnimation in correct way for adding a custom property to the sub?


Here is my working codes for callBack of CABasicAnimation when the animation is Done. I am given the needed action to animation delegate, but what I want to be able do this work on sub class of CABasicAnimation:

extension NSView {
    
    func fadeAnimation(callBack: @escaping () -> Void) {
        
        let animation = CABasicAnimation()
        
        animation.delegate = CustomCAAnimationDelegate(callBack: callBack)
        animation.keyPath = "opacity"
        animation.duration = 2.0
        animation.fromValue = 1.0
        animation.toValue = 0.0
        animation.timingFunction = CAMediaTimingFunction(name: .linear)
        animation.isRemovedOnCompletion = false
        animation.fillMode = .forwards
        
        self.layer?.add(animation, forKey: nil)

    }
}

class CustomCAAnimationDelegate: NSObject, CAAnimationDelegate {
    
    var callBack: () -> Void
    
     init(callBack: @escaping () -> Void) {
         self.callBack = callBack
    }
    
    internal func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
        callBack()
    }
    
}

So the goal is having some code like this:

class CustomCABasicAnimation: CABasicAnimation {
    
    var callBack: () -> Void

}

which if that is done I would be able to code like this:

extension NSView {
    
    func fadeAnimation(callBack: @escaping () -> Void) {
        
        let animation = CustomCABasicAnimation()
        
        animation.callBack = callBack
        
        // The other codes ...

    }
}

I have implemented my custom animation class as follows:

class CustomCABasicAnimation: CABasicAnimation {
    
    var callBack: () -> Void
    
    init(callBack: @escaping () -> Void) {
        self.callBack = callBack
        super.init()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}

When I run my code I get the error:

Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)

and:

Fade_Test/ViewController.swift:57: Fatal error: Use of unimplemented initializer 'init()' for class 'Fade_Test.CustomCABasicAnimation' 2023-03-17 23:39:59.577008+0100 Fade Test[1810:79953] Fade_Test/ViewController.swift:57: Fatal error: Use of unimplemented initializer 'init()' for class 'Fade_Test.CustomCABasicAnimation'


Solution

  • It turns out that extending CABasicAnimation isn't as straightforward as it would seem.

    You can't really add your own initializer. The documentation for CABasicAnimation states:

    You create an instance of CABasicAnimation using the inherited init(keyPath:) method, specifying the key path of the property to be animated in the render tree.

    So really your CustomCABasicAnimation can only add the new property, but no initializer. This means your callBack property must be optional.

    class CustomCABasicAnimation: CABasicAnimation {
        var callBack: (() -> Void)?
    }
    

    You can add/override other methods as needed to make use of the optional callBack.

    Now your creation of the CustomCABasicAnimation will have to look more like this:

    let animation = CustomCABasicAnimation(keyPath: "opacity")
    animation.callBack = callBack
    // other properties as needed