Change CALayer properties: EXC_BAD_INSTRUCTION

2019-07-27 23:08发布

问题:

I'm trying to implement selection of a subLayer by clicking on it and visualize that it is had been selected by for example changing the background color of the subLayer. However, I end up with the following error whatever and however I attempt to change any property:

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

Her is my function that is called from my NSViewsub class' mouseUpfunction.

func SelectObject( atPoint: NSPoint)
{
    let thePoint = atPoint as CGPoint
    if let hitLayer = itsCourseLayer.hitTest( thePoint) {
        if (hitLayer != itsCourseLayer) {
            // Gives following error:
            // Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
            hitLayer.backgroundColor = NSColor.red.cgColor 
        }
    }
}

I've also tried hitLayer.setValue( NSColor.red.cgColor, forKeyPath: "backgroundColor") and I've tried to surround it with CATransaction.begin() and CATransaction.commit(), or to change any other property of the subLayer. But with no success.

Any Idea what is wrong with my code?

回答1:

The error message is kind of telling you one solution: implement init(layer:) in CPCoursePointLayer.

When you set a property on a layer, and that layer is in a non-hidden window's layer tree, Core Animation looks for a CAAction for that property, by sending the actionForKey: message to the layer. If that search returns nil, Core Animation creates an implicit animation for the property using some default parameters.

Any layer with an attached animation has a corresponding presentation layer. (Read Layer Trees Reflect Different Aspects of the Animation State for more info.) So Core Animation needs to create the corresponding presentation layer for your hitLayer. It does this using the init(layer:) initializer on your CPCoursePointLayer class.

In Swift, a class doesn't automatically inherit all of its superclass's constructors, so your CPCoursePointLayer class doesn't have that initializer. So your app crashes.

One solution is to add the init(layer:) initializer to your CPCoursePointLayer class:

init(layer: CALayer) {
    let layer = layer as! CPCoursePointLayer
    // copy any custom properties from layer to self here
    super.init(layer: layer)
}

If you define init(layer:), then you can animate properties of your layer, and allow them to be implicitly animated.

If you never plan to explicitly or implicitly animate properties of your layer, then you can instead disable implicit animations by returning NSNull when Core Animation searches for an action, for example by adding this method to your CPCoursePointLayer class:

override class func defaultAction(forKey key: String) -> CAAction? {
    return unsafeBitCast(NSNull(), to: CAAction?.self)
}