Monday, June 11, 2012

How do I draw a shadow under a UIView?


I'm trying to draw a shadow under the bottom edge of a UIView in Cocoa Touch. I understand that I should use CGContextSetShadow() to draw the shadow, but the Quartz 2D programming guide is a little vague:



  1. Save the graphics state.

  2. Call the function CGContextSetShadow, passing the appropriate values.

  3. Perform all the drawing to which you want to apply shadows.

  4. Restore the graphics state



I've tried the following in a UIView subclass:




- (void)drawRect:(CGRect)rect {
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGContextSaveGState(currentContext);
CGContextSetShadow(currentContext, CGSizeMake(-15, 20), 5);
CGContextRestoreGState(currentContext);
[super drawRect: rect];
}



..but this doesn't work for me and I'm a bit stuck about (a) where to go next and (b) if there's anything I need to do to my UIView to make this work?


Source: Tips4all

4 comments:

  1. In your current code, you save the GState of the current context, configure it to draw a shadow .. and the restore it to what it was before you configured it to draw a shadow. Then, finally, you invoke the superclass's implementation of drawRect: .

    Any drawing that should be affected by the shadow setting needs to happen after

    CGContextSetShadow(currentContext, CGSizeMake(-15, 20), 5);


    but before

    CGContextRestoreGState(currentContext);


    So if you want the superclass's drawRect: to be 'wrapped' in a shadow, then how about if you rearrange your code like this?

    - (void)drawRect:(CGRect)rect {
    CGContextRef currentContext = UIGraphicsGetCurrentContext();
    CGContextSaveGState(currentContext);
    CGContextSetShadow(currentContext, CGSizeMake(-15, 20), 5);
    [super drawRect: rect];
    CGContextRestoreGState(currentContext);
    }

    ReplyDelete
  2. A by far easier approach is to set some layer attributes of the view on initialization:

    self.layer.masksToBounds = NO;
    self.layer.cornerRadius = 8; // if you like rounded corners
    self.layer.shadowOffset = CGSizeMake(-15, 20);
    self.layer.shadowRadius = 5;
    self.layer.shadowOpacity = 0.5;

    ReplyDelete
  3. self.layer.masksToBounds = NO;
    self.layer.cornerRadius = 8; // if you like rounded corners
    self.layer.shadowOffset = CGSizeMake(-15, 20);
    self.layer.shadowRadius = 5;
    self.layer.shadowOpacity = 0.5;


    This will slow down the application.
    Adding the following line can improve performance as long as your view is visibly rectangular:

    self.layer.shadowPath = [UIBezierPath bezierPathWithRect:self.bounds].CGPath;

    ReplyDelete
  4. I use this as part of my utils. With this we can not only set shadow but also can get a rounded corner for any UIView. Also you could set what color shadow you prefer. Normally black is preferred but sometimes, when the background is non-white you might want something else. Here's what I use -

    in utils.m
    + (void)roundedLayer:(CALayer *)viewLayer
    radius:(float)r
    shadow:(BOOL)s
    {
    [viewLayer setMasksToBounds:YES];
    [viewLayer setCornerRadius:r];
    [viewLayer setBorderColor:[RGB(180, 180, 180) CGColor]];
    [viewLayer setBorderWidth:1.0f];
    if(s)
    {
    [viewLayer setShadowColor:[RGB(0, 0, 0) CGColor]];
    [viewLayer setShadowOffset:CGSizeMake(0, 0)];
    [viewLayer setShadowOpacity:1];
    [viewLayer setShadowRadius:2.0];
    }
    return;
    }


    To use this we need to call this - [utils roundedLayer:yourview.layer radius:5.0f shadow:YES];

    ReplyDelete