Objective-c – NSView Drop Shadow Using setShadow:

cocoacore-animationdropshadownsviewobjective c

I'm attempting to make a drop shadow for a custom NSView subclass.

So far, I've managed:

- (id)initWithFrame:(NSRect)frame
{
    self = [super initWithFrame:frame];
    if (self)
    {
        NSShadow *dropShadow = [[NSShadow alloc] init];
        [dropShadow setShadowColor: [NSColor redColor]];

        [self setWantsLayer: YES];
        [self setShadow: dropShadow];
    }

    return self;
}

- (void)drawRect:(NSRect)dirtyRect
{
    [[NSColor blueColor] setFill];
    NSRectFill(dirtyRect);

    [super drawRect: dirtyRect];
}

which only renders a blue square (i.e. no shadow).

Am I setting up the drop shadow in the right place?
Am I meeting all of the necessary requirements for the use of setShadow:?

Best Answer

A few notes before answering the question:

  • You don't need to call super's implementation of drawRect: on a vanilla NSView. The default implementation does nothing.
  • You should be using [self bounds] as the fill rectangle, not dirtyRect. The dirtyRect parameter is used to indicate the part of the view that needs drawing and is used for drawing optimisation only.
  • You are leaking the dropShadow object. You should either call autorelease on it after creation or call release on it after calling setShadow:.

The reason that the shadow isn't displaying are twofold. Firstly, in order for layer-backed views to display a shadow, the view's superview must also be layer-backed.

Secondly, you're setting the shadow's color but not its other parameters:

- (id)initWithFrame:(NSRect)frame
{
    self = [super initWithFrame:frame];
    if (self)
    {
        NSShadow *dropShadow = [[NSShadow alloc] init];
        [dropShadow setShadowColor:[NSColor redColor]];
        [dropShadow setShadowOffset:NSMakeSize(0, -10.0)];
        [dropShadow setShadowBlurRadius:10.0];

        [self setWantsLayer: YES];
        [self setShadow: dropShadow];

        [dropShadow release];
    }

    return self;
}