iOS Programming Recipe 10: Adding A Shadow To UIView

Assumptions

Shadows!

Even the slightest drop shadow here and there can dramatically improve the look of your application’s UI, but at what cost?

Today we will cover adding shadows to UIViews of all kinds. Feel free to check out the source code for this recipe available on our GitHub page.

Step 1: Setup A Dummy Project
  • Open Xcode, create a new project using the single view based iOS application template. Name it something like “Shadows” and be sure to uncheck Use Storyboards and check Use Automatic Reference Counting. I also selected iPhone from the devices drop-down menu, just to keep things simple.

1

  • Next, open ViewController.xib and add a generic UIView to the view controller’s view.
  • Let’s go ahead and turn off Autolayout for this xib file by opening the File Inspector Option + CMD + 1 and unchecking Use Autolayout.
  • With the newly added UIView selected, open the size inspector Option + CMD + 5 and remove all the springs and struts from the view. Then, resize it to 200×200 and center it within the superview.

2

  • Next add an outlet for the view in ViewController.h, then connect it in viewController.xib by Control + Dragging from File’s Owner to the view and selecting sampleView from the HUD.

@property (nonatomic, strong) IBOutlet UIView *sampleView;
  • Next change the background color of sampleView in viewController.xib to blue by selecting it in the object tree and then changing the background color in the attributes inspector Option + CMD + 4.
  • Build and run the project CMD + R, you should see a blue square in the center of the screen if everything has been setup successfully.
Step 2: Adding A Basic Shadow
  • First, we need to add the QuartzCore framework to the project. This is where Core Animation lives, which is what we will be using to create our shadows. To add QuartzCore to the project, select the project file from the source list on the left, then select the Build Phases tab in the center and open the Link Libraries With Binary section.

3

  • Press the + button in the lower left corner of the section, then type QuartzCore in the search field, select it and press Add in the lower right corner.

4

  • Open ViewController.h and add the following import for QuartzCore at the top. This will allow us to use Core Animation API.

#import <QuartzCore/QuartzCore.h>
  • Now open viewController.m, In the viewDidLoad method we will add our shadow code.

- (void)viewDidLoad
{
    [super viewDidLoad];

    //Adds a shadow to sampleView
    CALayer *layer = self.sampleView.layer;
    layer.shadowOffset = CGSizeMake(1, 1);
    layer.shadowColor = [[UIColor blackColor] CGColor];
    layer.shadowRadius = 4.0f;
    layer.shadowOpacity = 0.80f;
    layer.shadowPath = [[UIBezierPath bezierPathWithRect:layer.bounds] CGPath];
}
  • Build & run the app, you now have a shadow around the view!

5

Step 3: Shadows Explained

As seen in the last step there are a few different properties needed when adding a shadow using Core Animation. If your are not familiar with Core Animation don’t worry, just try to focus on the shadow code and not the CALayer on the view.

  • shadowOffset is a CGSize representing how far to offset the shadow from the path.
  • shadowColor is the color of the shadow. Shadow colors should always be opaque, because the opacity will be set by the shadowOpacity property. The shadowColor property is a CGColor not a UIColor.
  • shadowRadius is the width of the shadow along the shadow path
  • shadowOpacity determines the opacity of the shadow.
  • shadowPath is probably the most important of the properties. While a shadow can be drawn without specifying a path, for performance reasons you should always specify one. This path tells Core Animation what the opaque regions of the view are, and without it, things slow down severely! It is a CGPath, which is most easily created using UIBezierPath (iOS only) as shown in step 2.
Step 4: Fun With Shadow Paths

One of the cool things about adding shadows using Core Animation is that the shadowPath doesn’t have to be a square! Let’s create a quick method that creates a shadowPath that makes the view appear to bend in the center.


- (CGPathRef)fancyShadowForRect:(CGRect)rect
{
    CGSize size = rect.size;
    UIBezierPath* path = [UIBezierPath bezierPath];

    //right
    [path moveToPoint:CGPointZero];
    [path addLineToPoint:CGPointMake(size.width, 0.0f)];
    [path addLineToPoint:CGPointMake(size.width, size.height + 15.0f)];

    //curved bottom
    [path addCurveToPoint:CGPointMake(0.0, size.height + 15.0f)
            controlPoint1:CGPointMake(size.width - 15.0f, size.height)
            controlPoint2:CGPointMake(15.0f, size.height)];

    [path closePath];

    return path.CGPath;
}
  • Add this method to viewController.m and then change viewDidLoad as follows

- (void)viewDidLoad
{
    [super viewDidLoad];

    //Adds a shadow to sampleView
    CALayer *layer = self.sampleView.layer;

    //changed to zero for the new fancy shadow
    layer.shadowOffset = CGSizeZero;

    layer.shadowColor = [[UIColor blackColor] CGColor];

    //changed for the fancy shadow
    layer.shadowRadius = 2.0f;

    layer.shadowOpacity = 0.80f;

    //call our new fancy shadow method
    layer.shadowPath = [self fancyShadowForRect:layer.bounds];
}
  • Build and run, look at the new fancy shadow!

6

Final Thoughts

Adding shadows to views isn’t very hard using Core Animation and it can really make your app look great! Just don’t over do it! Remember shadows, even with a shadow path specified, do affect performance negatively so use them appropriately. If you are interested in more cool shadow paths take a look at this posting!

As always, please let us know if you have any questions or comments!

Comments

  1. How about shadows with views where:
    layer.masksToBounds = NO;
    layer.cornerRadius = 2.5f;

    When you mask to bounds, the shadow is masked out, but it masks to the rounded corners. When you don’t mask to bounds, the shadow is there but there are no rounded corners.

  2. layer.masksToBounds = YES;*

    • Great question!

      Unfortunately, there isn’t an easy answer to that problem…The fundamental issue is that maskToBounds or UIView’s clipToBounds also masks your shadow because the shadow is drawn outside of your view’s (or layer’s) bounds.

      There are three ways that I know of to get around this issue, but none are particularly elegant.

      • 1. Introduce another view! Add your view as a subview of a view that draws the shadow. That way your view’s mask will not affect the shadow.
      • 2. Create a small resizable asset that incorporates the rounded corners (you could even do this in code and cache it) and add it in the background of your view, then set your view’s background transparent. This will also help performance because there will be no need for the mask at all!
      • 3. Use drawRect. This way would not have the best performance, so I would avoid it if possible but it will work.

      This is a great question and I would love to hear if anyone else has come up with a different/better solution.

Speak Your Mind

*

css.php
Privacy Policy