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.
First, let us consider a property of UIImageView named ‘imageView’ present in storyboard, on which all the shadows will be implemented.
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
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
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
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
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
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
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.
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.