Implementing shadow in iOS

Swarnendu De August 25, 2015

I have been constantly developing iOS apps for the last two years of my life. Doing so, I often came with client requirements where shadows become the main factor for the UI, giving a 3D look and feel to the whole interface. Implementing shadow in iOS is quite easy but there are times, when shadows are complex to deal with and require some brainstorming. Today we will see the implementation of shadows (6-7 types), in Swift as well as Objective-C.

Implementation

First, let us consider a property of UIImageView named ‘imageView’ present in storyboard, on which all the shadows will be implemented.

1. Normal shadow with an offset

If you only apply an offset to the view layer, it will introduce a two-way border shadow. Changing the offset value with the “+” and “-” signs for both width and height will generate two way bordered shadow, according to the applied changes.

Objective-C 

_imageView.layer.shadowColor = [UIColor colorWithWhite:0.0 alpha:0.5].CGColor;
_imageView.layer.shadowOffset = CGSizeMake(3.0, 3.0);
_imageView.layer.shadowOpacity = 1.0;
_imageView.layer.shadowRadius = 3.0;

Swift 

imageView.layer.shadowColor = UIColor(white: 0.0, alpha: 0.5).CGColor
imageView.layer.shadowOffset = CGSizeMake(3.0, 3.0)
imageView.layer.shadowOpacity = 1.0
imageView.layer.shadowRadius = 3.0

And this is what a normal shadow with offset will look like
shadow 1

2. Overall shadow to Image  

Removing the offset value or setting it to zero will make the shadows appear on all the sides of your view.

Objective-C 

//applying overall shadow to image
_imageView.layer.shadowColor = kShadowColor.CGColor;
_imageView.layer.shadowOffset = CGSizeMake(0.0, 0.0);
_imageView.layer.shadowOpacity = 1.0;
_imageView.layer.shadowRadius = 6.0;

Swift 

//applying overall shadow to image
imageView.layer.shadowColor = UIColor(white: 0.0, alpha: 0.5).CGColor
imageView.layer.shadowOffset = CGSizeMake(0.0, 0.0)
imageView.layer.shadowOpacity = 1.0
imageView.layer.shadowRadius = 6.0

The overall shadow would look like this
shadow 3

3. Trapezoidal shadow

Creating a trapezoidal shadow below a view will create an impression of the view hanging above, with an in depth shadow below it. Playing around with UIBezierPath can help you achieve this effect and many others.

Objective-C 

CGSize size = _imageView.bounds.size;
    
//creating a trapezoidal path for shadow
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(size.width * 0.20f, size.height * 0.80f)];
[path addLineToPoint:CGPointMake(size.width * 0.80f, size.height * 0.80f)];
[path addLineToPoint:CGPointMake(size.width * 1.20f, size.height * 1.20f)];
[path addLineToPoint:CGPointMake(size.width * -0.20f, size.height * 1.20f)];
[path closePath];

//applying path as shadow to image
_imageView.layer.shadowColor = kShadowColor.CGColor;
_imageView.layer.shadowOffset = CGSizeMake(3.0, 3.0);
_imageView.layer.shadowOpacity = 1.0;
_imageView.layer.shadowRadius =2.0;
_imageView.layer.shadowPath = path.CGPath;

Swift 

var size = imageView.bounds.size

//creating a trapezoidal path for shadow
var path = UIBezierPath()
path.moveToPoint(CGPointMake(size.width * 0.20, size.height * 0.80))
path.addLineToPoint(CGPointMake(size.width * 0.80, size.height * 0.80))
path.addLineToPoint(CGPointMake(size.width * 1.20, size.height * 1.20))
path.addLineToPoint(CGPointMake(size.width * -0.20, size.height * 1.20))
path.closePath()

//applying path as shadow to image
imageView.layer.shadowColor = UIColor(white: 0.0, alpha: 0.5).CGColor
imageView.layer.shadowOffset = CGSizeMake(3.0, 3.0)
imageView.layer.shadowOpacity = 1.0
imageView.layer.shadowRadius = 2.0
imageView.layer.shadowPath = path.CGPath

Below is the image of a trapezoidal shadow
shadow 2

4. Elliptical shadow

It will create a shadow generating the same impact as above, but with a different shadow type(or design), done with UIBezierPath.

Objective-C

//create elliptical shadow for image through UIBezierPath
CGRect ovalRect = CGRectMake(0.0f, _imageView.frame.size.height + 10, _imageView.frame.size.width, 15);
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:ovalRect];
    
//applying shadow to path
_imageView.layer.shadowColor = kShadowColor.CGColor;
_imageView.layer.shadowOffset = CGSizeMake(0.0, 0.0);
_imageView.layer.shadowOpacity = 1.0;
_imageView.layer.shadowRadius = 3.0;
_imageView.layer.shadowPath = path.CGPath;

Swift 

//create elliptical shdow forimage through UIBezierPath
var ovalRect = CGRectMake(0.0, imageView.frame.size.height + 10, imageView.frame.size.width, 15)
var path = UIBezierPath(ovalInRect: ovalRect)
        
//applying shadow to path
imageView.layer.shadowColor = UIColor(white: 0.0, alpha: 0.5).CGColor
imageView.layer.shadowOffset = CGSizeMake(0.0, 0.0)
imageView.layer.shadowOpacity = 1.0
imageView.layer.shadowRadius = 3.0
imageView.layer.shadowPath = path.CGPath

This is what an Elliptical shadow looks like
shadow 4

5. Curl shadow

It will create an impact as if your view is on a folded superview, posing the shadow of the earlier.

Objective-C 

//create a bezier path with curl effect
CGSize size = _imageView.frame.size;
UIBezierPath* path = [UIBezierPath bezierPath];
    
//starting from left point
[path moveToPoint:CGPointMake(0.0, size.height)];
[path addLineToPoint:CGPointMake(0.0, size.height + 20.0f)];
    
//curved bottom part
[path addCurveToPoint:CGPointMake(size.width, size.height + 20.0f)
            controlPoint1:CGPointMake(20.0f, size.height)
            controlPoint2:CGPointMake(size.width - 20.0f, size.height)];
    
//closing the path by going upper top part
[path addLineToPoint:CGPointMake(size.width, size.height)];

//close the path and apply it as shadow
[path closePath];
    
//applying shadow to imageView through the path created
_imageView.layer.shadowColor = kShadowColor.CGColor;
_imageView.layer.shadowOffset = CGSizeMake(0.0, 0.0);
_imageView.layer.shadowOpacity = 1.0;
_imageView.layer.shadowRadius = 3.0;
_imageView.layer.shadowPath = path.CGPath;

Swift 

//create a bezier path with curl effect
var size = imageView.frame.size
var path = UIBezierPath()
        
//starting from left point
path.moveToPoint(CGPointMake(0.0, size.height))
path.addLineToPoint(CGPointMake(0.0, size.height + 20.0))
        
//curved bottom part
path.addCurveToPoint(CGPointMake(size.width, size.height + 20.0), controlPoint1: CGPointMake(20.0, size.height), controlPoint2: CGPointMake(size.width - 20.0, size.height))

//closing the path by going upper top part
path.addLineToPoint(CGPointMake(size.width, size.height))
        
//close the path and apply the path as shadow
path.closePath()
        
//applying shadow to imageView through the path created
imageView.layer.shadowColor = UIColor(white: 0.0, alpha: 0.5).CGColor
imageView.layer.shadowOffset = CGSizeMake(0.0, 0.0);
imageView.layer.shadowOpacity = 1.0
imageView.layer.shadowRadius = 3.0
imageView.layer.shadowPath = path.CGPath

A Curl shadow looks like this
shadow 6

6. Animated shadows

Shadows can be animated very easily by making it look as if light source is focussing on your view while moving around. Playing around with values like animationWithKeyPath, fromValue and toValue, you can bring variations in the animation as you desire.

Objective-C

//applying the shadow
_imageView.layer.shadowColor = kShadowColor.CGColor;
_imageView.layer.shadowOffset = CGSizeMake(20.0, -20.0);
_imageView.layer.shadowOpacity = 1.0;
_imageView.layer.shadowRadius = 2.0;
    
//providing animation to shadow
CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:@"shadowOffset"];
//change the fromValue and toValue as per your animation requirement   
animation.fromValue = [NSValue valueWithCGSize:CGSizeMake(-20.0, -20.0)];
animation.toValue = [NSValue valueWithCGSize:CGSizeMake(20.0, -20.0)];
    animation.duration = 2.0;
[_imageView.layer addAnimation:animation forKey:@"shadowOffset"];

Swift 

//applying the shadow
imageView.layer.shadowColor =  UIColor(white: 0.0, alpha: 0.5).CGColor
imageView.layer.shadowOffset = CGSizeMake(20.0, -20.0)
imageView.layer.shadowOpacity = 1.0
imageView.layer.shadowRadius = 2.0
    
//applying animation to shadows
var animation = CABasicAnimation(keyPath: "shadowOffset")
animation.fromValue = NSValue(CGSize:CGSizeMake(-20.0, -20.0))
animation.toValue = NSValue(CGSize: CGSizeMake(20.0, -20.0))
animation.duration = 2.0
imageView.layer.addAnimation(animation, forKey: "shadowOffset")

Animated shadow looks like this
shadow

7. Shadow for view with rounded corners

While finding a solution to this question, I found its solution on stackoverflow which is to implement a shadow view behind the actual view you are working on.

Objective-C 

- (UIView*)putView:(UIView*)view insideShadowWithColor:(UIColor*)color andRadius:(CGFloat)shadowRadius andOffset:(CGSize)shadowOffset andOpacity:(CGFloat)shadowOpacity
{
    CGRect shadowFrame; // Modify this if needed
    shadowFrame.size.width = 0.f;
    shadowFrame.size.height = 0.f;
    shadowFrame.origin.x = 0.f;
    shadowFrame.origin.y = 0.f;
    UIView * shadow = [[UIView alloc] initWithFrame:shadowFrame];
    shadow.userInteractionEnabled = NO; // Modify this if needed
    shadow.layer.shadowColor = color.CGColor;
    shadow.layer.shadowOffset = shadowOffset;
    shadow.layer.shadowRadius = shadowRadius;
    shadow.layer.masksToBounds = NO;
    shadow.clipsToBounds = NO;
    shadow.layer.shadowOpacity = shadowOpacity;
    [view.superview insertSubview:shadow belowSubview:view];
    [shadow addSubview:view];
    return shadow;
}

Swift

func putShadowOnView(viewToWorkUpon:UIView, shadowColor:UIColor, radius:CGFloat, offset:CGSize, opacity:Float)-> UIView{
        
        var shadowFrame = CGRectZero // Modify this if needed
        shadowFrame.size.width = 0.0
        shadowFrame.size.height = 0.0
        shadowFrame.origin.x = 0.0
        shadowFrame.origin.y = 0.0
    
        var shadow = UIView(frame: shadowFrame)//[[UIView alloc] initWithFrame:shadowFrame];
        shadow.userInteractionEnabled = false; // Modify this if needed
        shadow.layer.shadowColor = shadowColor.CGColor
        shadow.layer.shadowOffset = offset
        shadow.layer.shadowRadius = radius
        shadow.layer.masksToBounds = false
        shadow.clipsToBounds = false
        shadow.layer.shadowOpacity = opacity
        viewToWorkUpon.superview?.insertSubview(shadow, belowSubview: viewToWorkUpon)
        shadow.addSubview(viewToWorkUpon)
        return shadow
    }

Just apply rounded corner to your view and call the function on it and you will see how well it works.

Below you can see an image for shadow upon view, with rounded corners.
shadow 5

You can find sample project for shadow implementation in Swift and Objective-C in GitHub.

Hope this tutorial was helpful. Please feel free to come up with interesting ideas and concepts for shadow and leave a comment below for us to check them out.