Small gotcha when using NSView as delegate for CALayer

January 7, 2010

I ran into another small unexpected behavior when working on some Core Animation code recently. I had a subclass of NSView that acted as a layer-hosting view. The layers that the view hosted had the view set as delegate to do some simple drawing.

I didn't want the layers to animate their bounds so I did the usual:

[layer setActions:[NSDictionary dictionaryWithObjectsAndKeys:
                   [NSNull null], @"bounds",
                   [NSNull null], @"position",
                   nil]];

...which to my surprise didn't work. After digging around for a while I found out that NSView implements actionForLayer:forKey, which has precedence over the actions dictionary. The quick and dirty solution was to override it and return a null action for the relevant keys:

- (id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event
{
    if ([event isEqualToString:@"position"] || [event isEqualToString:@"bounds"]) {
        return (id<CAAction>)[NSNull null];
    }

    return nil;
}

However, my guess is that NSView is not really meant to be used as delegate for its hosted layers like this. A better design in most cases would be to either subclass the layer to do its own drawing, or to use a separate controller for event handling or other book keeping for the layer(s).

So in reality this is not really a problem but I thought I'd post it here in case someone else runs into this problem.