Adding Shadows on iOS

Posted by Grego on April 24, 2015

Side note

A while back in November I wrote a post on my other blog (which predates this one) titled Adding [Text] Shadows on iOS. That was back before I had a dedicated website for development posts. Today I’m going to migrate that post here, practically verbatim. I apologize for the repost, but I was looking for this post the other day and it took me a while to realize it was on a different site all together :)

Since I was feeling extra ambitious I uploaded the code with a small sample XCode project to github. Check it out at hk0i/UIView-DropShadow.

Adding Shadows in iOS

Although it’s very simple, I often forget how to add a shadow in iOS using Objective-C. It’s not so much that I forget, but that there are certain required parameters that need to be set in order for it to work.

So I’ll post some examples here.

Using CALayer on a UILabel (Should work with any UIView subclass):

self.lblChampionName.layer.shadowOffset = CGSizeMake(0, 3); //default is (0.0, -3.0)
self.lblChampionName.layer.shadowColor = [UIColor blackColor].CGColor; //default is black
self.lblChampionName.layer.shadowRadius = 1.0; //default is 3.0
self.lblChampionName.layer.shadowOpacity = .5; //default is 0.0

Important Notes:

  • CALayer’s shadowColor property takes a CGColor not a UIColor.
  • You can convert UIColor to CGColor using UIColor’s CGColor property as shown above.
  • shadowOpacity defaults to 0.0, if you don’t set it, you will not see your shadow.

Check the CALayer documentation for more information on the default values and their types.

Adding to a UIButton:

self.btnRollTheDice.titleLabel.shadowOffset = CGSizeMake(0, 2);
//setting stately shadow color, also takes a UIColor, not a CGColor.
[self.btnRollTheDice setTitleShadowColor:[UIColor blackColor] forState:UIControlStateNormal];

Adding to a UILabel without CALayer:

self.lblChampionName.shadowOffset = CGSizeMake(0, 2);
self.lblChampionName.shadowColor = [UIColor blackColor]; //takes a UIColor

Adding to a UIButton without CALayer:

self.btnRollTheDice.titleLabel.layer.shadowOffset = CGSizeMake(0, 2);
self.btnRollTheDice.titleLabel.layer.shadowColor = [UIColor blackColor].CGColor;
self.btnRollTheDice.titleLabel.layer.shadowOpacity = .5;

Adding drop shadow to a UIView.

If you want to add a drop shadow to a UIView (not a text shadow) you can apply the same CALayer approach as detailed in the first example.

Tidying up

Usually if you have one drop shadow (or text shadow) you want more. We can tidy up and centralize that using a Category in objective-c. See also: Programming with Objective-C: Customizing Existing Classes.

In Xcode 6, select File > New > Objective-C File then select Category as your file type. I named mine DropShadow. Type UIView under Class. This creates the appropriate UIView+DropShadow files.

This is what my code looks like:

UIView+DropShadow.h:

#import <UIKit/UIKit.h>

@interface UIView (DropShadow)

- (void)addDropShadow:(UIColor *)color
  withOffset:(CGSize)offset
  radius:(CGFloat)radius
  opacity:(CGFloat)opacity;

@end

And the implementation file,
UIView+DropShadow.m:

#import "UIView+DropShadow.h"

@implementation UIView (DropShadow)
- (void)addDropShadow:(UIColor *)color
        withOffset:(CGSize)offset
        radius:(CGFloat)radius
        opacity:(CGFloat)opacity
{
  self.layer.shadowColor = color.CGColor;
  self.layer.shadowOffset = offset;
  self.layer.shadowRadius = radius;
  self.layer.shadowOpacity = opacity;
}

@end

and a sample invocation:

  [self.lblChampionName addDropShadow:[UIColor blackColor]
                        withOffset:CGSizeMake(0, 3)
                        radius:1.0f
                        opacity:.5];

It’s ultimately the same amount of lines, however now we have autocompletion and tabbing.

The next step would be to add some sensible defaults and convenience methods. I added one to our new category, for example:

const CGSize DS_DEFAULT_OFFSET = {0, 2};
const CGFloat DS_DEFAULT_RADIUS = 1.0f;
const CGFloat DS_DEFAULT_OPACITY = 0.8f;

- (void)addDropShadow:(UIColor *)color
{
  [self addDropShadow:color
        withOffset:DS_DEFAULT_OFFSET
        radius:DS_DEFAULT_RADIUS
        opacity:DS_DEFAULT_OPACITY];
}

And now I can add shadows like this:

UIColor *shadowColor = [UIColor blackColor];

[self.btnRollTheDice addDropShadow:shadowColor];
[self.lblChampionName addDropShadow:shadowColor
                      withOffset:CGSizeMake(0, 3)
                      radius:1.0f
                      opacity:.5];

Learn more about const CGStruct and the possible caveats of using the initializer list.

If you wanted to, you could probably add some sort of class methods to change the defaults, that way you could on a per-project basis change your default shadow arguments without too much trouble. But I won’t go into that for now.