I saw a demo somewhere, where some dude created different nifty shadow effects by tweaking the shadowPath property of UIView’s CALayer. Being pretty new to Swift and iOS development, I figured it would be fun to play around with it – here’s the result (screenshot from the iPhone simulator):
Here’s how I did it – first, I installed this simple UIViewController to display the four images:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
class ShadowPathsController : UIViewController { override func viewDidLoad() { super.viewDidLoad() var bare = createView(CGPoint(x: 40, y: 50), label: "bare") var plain = createView(CGPoint(x: 180, y: 50), label: "plain") applyPlainShadow(plain); var curved = createView(CGPoint(x: 40, y: 200), label: "curved") applyCurvedShadow(curved) var hover = createView(CGPoint(x: 180, y: 200), label: "hover") applyHoverShadow(hover) } func createView(position: CGPoint, label: String) -> UIView { var imageView = UIImageView(image: UIImage(named: "yoda")) imageView.frame = CGRect(origin: position, size: CGSize(width: 100, height: 100)) var labelView = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 20)) labelView.textAlignment = NSTextAlignment.Center labelView.text = label labelView.textColor = UIColor.whiteColor() imageView.addSubview(labelView) view!.addSubview(imageView); return imageView } // apply* methods are down here } |
and then I created the applyPlainShadow method (which is basically the shadow example you’ll find everywhere when you Google for “UIView drop shadow”):
1 2 3 4 5 6 7 8 |
func applyPlainShadow(view: UIView) { var layer = view.layer! layer.shadowColor = UIColor.blackColor().CGColor layer.shadowOffset = CGSize(width: 0, height: 10) layer.shadowOpacity = 0.4 layer.shadowRadius = 5 } |
Simple and neat… – but now the fun begins – let’s create that convex-piece-of-paper-on-a-surface-effect that you’ll see in many places on the web – it’s created by simply using a UIBezierPath to draw the outline of the shadow, and then setting the layer’s shadowPath to the path’s CGRect:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
func applyCurvedShadow(view: UIView) { let size = view.bounds.size let width = size.width let height = size.height let depth = CGFloat(11.0) let lessDepth = 0.8 * depth let curvyness = CGFloat(5) let radius = CGFloat(1) var path = UIBezierPath() // top left path.moveToPoint(CGPoint(x: radius, y: height)) // top right path.addLineToPoint(CGPoint(x: width - 2*radius, y: height)) // bottom right + a little extra path.addLineToPoint(CGPoint(x: width - 2*radius, y: height + depth)) // path to bottom left via curve path.addCurveToPoint(CGPoint(x: radius, y: height + depth), controlPoint1: CGPoint(x: width - curvyness, y: height + lessDepth - curvyness), controlPoint2: CGPoint(x: curvyness, y: height + lessDepth - curvyness)) var layer = view.layer! layer.shadowPath = path.CGPath layer.shadowColor = UIColor.blackColor().CGColor layer.shadowOpacity = 0.3 layer.shadowRadius = radius layer.shadowOffset = CGSize(width: 0, height: -3) } |
Neat! Lastly, let’s create a blurry oval drop shadow, as if the view was hovering high over some low surface that expands into the screen… again, I’m using the shadowPath property, initializing the UIBezierPath with the overload that creates an oval shape from a rectangle:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
func applyHoverShadow(view: UIView) { let size = view.bounds.size let width = size.width let height = size.height var ovalRect = CGRect(x: 5, y: height + 5, width: width - 10, height: 15) var path = UIBezierPath(roundedRect: ovalRect, cornerRadius: 10) var layer = view.layer! layer.shadowPath = path.CGPath layer.shadowColor = UIColor.blackColor().CGColor layer.shadowOpacity = 0.2 layer.shadowRadius = 5 layer.shadowOffset = CGSize(width: 0, height: 0) } |
Nice!