Search code examples
core-graphicscocoa-bindingskey-value-coding

Cocoa bindings and KVC with CGColorRef


I'm trying to bind a CGColorRef on one of my objects to the "shadowColor" property of a CALayer. Unfortunately, I haven't been able to figure this out - it's probably something really simple!

The CGColorRef is implemented as a property:

@property (readwrite) CGColorRef labelShadowColor;

My binding is straight forward too:

[aLayer bind:@"shadowColor" toObject:aScreen withKeyPath:@"labelShadowColor" options:nil];

Where I'm coming unstuck is valueForUndefinedKey: - how would I implement this for a CGColorRef? I'm currently getting the boilerplate:

2009-08-09 03:13:50.056 Hyperspaces[33161:a0f] An uncaught exception was raised
2009-08-09 03:13:50.060 Hyperspaces[33161:a0f] [<HSScreen 0x100533930> valueForUndefinedKey:]: this class is not key value coding-compliant for the key labelShadowColor.
2009-08-09 03:13:50.064 Hyperspaces[33161:a0f] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<HSScreen 0x100533930> valueForUndefinedKey:]: this class is not key value coding-compliant for the key labelShadowColor.'

I can work around this by putting an NSColor property on both ends and setting the CALayer's "shadowColor" manually whenever the color is changed, but that seems inelegant.


Solution

  • OK, so here's a tip that I missed (and how I solved it):

    You can't synthesize CGColorRefs (@synthesize someProperty;) - you need to declare the property @dynamic and implement the getters/setters, like so:

    @dynamic labelShadowColor;
    - (CGColorRef)labelShadowColor {
        return labelShadowColor;
    }
    
    - (void)setLabelShadowColor:(CGColorRef)aShadowColor {
        if (CGColorEqualToColor(labelShadowColor,aShadowColor)) return;
    
        CGColorRelease(labelShadowColor);
        if (aShadowColor != NULL) {
            labelShadowColor = CGColorRetain(aShadowColor);
        }
    }
    

    Then you'll also need to define valueForUndefinedKey: in your class:

    - (id)valueForUndefinedKey:(NSString *)key {
      if ([key isEqualToString:@"labelShadowColor"]) {
        return (id)self.labelShadowColor;
      }
    
      return [super valueForUndefinedKey:key];
    }
    

    Once these two things were done, my bindings sprang into action!