# [HowTo] Advanced UIVIew Shadow Effects
All subclasses of UIView, plus UIView itself, have access to a layer property where we can apply shadow effects directly in code. There are lots of parameters we can tweak:
- shadowColor: controls the color of the shadow, and can be used to make shadows (dark colors) or glows (light colors). This defaults to black.
- shadowOffset: controls how far the shadow is moved away from its view. This defaults to 3 points up from the view. You can think of where the lights are shining.
- shadowOpacity: controls how transparent the shadow is. This defaults to 0, meaning “invisible”.
- shadowPath: controls the shape of the shadow. This defaults to nil, which causes UIKit to render the view offscreen to figure out the shadow shape.
- shadowRadius: controls how blurred the shadow is. This defaults to 3 points.
Making UIKit calculate the shadow path dynamically is significantly slower than explicitly setting the value, so if you use shadows it’s usually a good idea to set the path. Even better, you can create interesting shapes with the path to make different kinds of shadows.
# Default shadows
let width: CGFloat = 200
let height: CGFloat = 200
// Better to set the frame value at the beginning.
let vw = UIView(frame: CGRect(x: 0, y: 0, width: width, height: height))
vw.center = view.center
vw.backgroundColor = .white
view.addSubview(vw)
vw.layer.shadowOpacity = 1
vw.layer.shadowPath = UIBezierPath(rect: vw.bounds).cgPath
vw.layer.shadowRadius = 5
vw.layer.shadowOffset = .zero
vw.layer.shadowOpacity = 1
# Contact shadows
A contact shadow is one that makes it look like our view is near to or even touching a surface, so the shadow exists directly below the view rather than around it.
let shadowSize: CGFloat = 20
let contactRect = CGRect(x: -shadowSize, y: height - (shadowSize * 0.4), width: width + shadowSize * 2, height: shadowSize)
vw.layer.shadowPath = UIBezierPath(ovalIn: contactRect).cgPath
vw.layer.shadowRadius = 5
vw.layer.shadowOpacity = 0.4
let shadowSize: CGFloat = 20
let shadowDistance: CGFloat = 20
let contactRect = CGRect(x: -shadowSize, y: height - (shadowSize * 0.4) + shadowDistance, width: width + shadowSize * 2, height: shadowSize)
vw.layer.shadowPath = UIBezierPath(ovalIn: contactRect).cgPath
vw.layer.shadowRadius = 5
vw.layer.shadowOpacity = 0.4
# Depth shadows
You can add a 3D effect to your view by casting a shadow in front of it. This is done by creating another custom UIBezierPath shadow path where we position the top of the shadow at the bottom of our view, and the bottom of the shadow some distance further down. We then bring the bottom edges of the shadow outwards so that it’s wider on the bottom than it is at the top, giving a perspective effect.
let shadowRadius: CGFloat = 5
// how wide and high the shadow should be, where 1.0 is identical to the view
let shadowWidth: CGFloat = 1.25
let shadowHeight: CGFloat = 0.5
let shadowPath = UIBezierPath()
shadowPath.move(to: CGPoint(x: shadowRadius / 2, y: height - shadowRadius / 2))
shadowPath.addLine(to: CGPoint(x: width - shadowRadius / 2, y: height - shadowRadius / 2))
shadowPath.addLine(to: CGPoint(x: width * shadowWidth, y: height + (height * shadowHeight)))
shadowPath.addLine(to: CGPoint(x: width * -(shadowWidth - 1), y: height + (height * shadowHeight)))
vw.layer.shadowPath = shadowPath.cgPath
vw.layer.shadowRadius = shadowRadius
vw.layer.shadowOffset = .zero
vw.layer.shadowOpacity = 0.2
# Flat, long shadows
This shadow effect is particularly prevalent in modern design, where shadows are more of a design feature rather than providing any sort of function. Here, the shadow is sharp-edged and moves continuously off into the infinite distance at 45-degree angle.
vw.layer.shadowRadius = 0
vw.layer.shadowOffset = .zero
vw.layer.shadowOpacity = 0.2
// how far the bottom of the shadow should be offset
let shadowOffsetX: CGFloat = 2000
let shadowPath = UIBezierPath()
shadowPath.move(to: CGPoint(x: 0, y: height))
shadowPath.addLine(to: CGPoint(x: width, y: height))
// make the bottom of the shadow finish a long way away, and pushed by our X offset
shadowPath.addLine(to: CGPoint(x: width + shadowOffsetX, y: 2000))
shadowPath.addLine(to: CGPoint(x: shadowOffsetX, y: 2000))
vw.layer.shadowPath = shadowPath.cgPath
view.backgroundColor = UIColor(red: 230 / 255, green: 126 / 255, blue: 34 / 255, alpha: 1.0)
# Extensions
extension UIView {
func makeEdges(cornerRadius: CGFloat, borderWidth: CGFloat, _ color: CGColor = UIColor.white.cgColor) {
layer.cornerRadius = cornerRadius
layer.borderWidth = borderWidth
layer.borderColor = color
}
func makeShadow(opacity: Float, radius: CGFloat, _ color: CGColor = UIColor.systemGray4.withAlphaComponent(0.7).cgColor) {
layer.shadowColor = color
layer.shadowOpacity = opacity
layer.shadowRadius = radius
layer.shadowOffset = CGSize(width: 0, height: 0)
}
func makeShadowWithCAShapeLayer(roundedRect: CGRect, opacity: Float, radius: CGFloat) {
let shadowLayer = CAShapeLayer()
shadowLayer.path = UIBezierPath(roundedRect: roundedRect, cornerRadius: radius).cgPath
shadowLayer.fillColor = UIColor.black.cgColor
shadowLayer.shadowColor = UIColor.systemGray4.cgColor
shadowLayer.shadowPath = shadowLayer.path
shadowLayer.shadowOffset = CGSize(width: 0.0, height: 0.0)
shadowLayer.shadowOpacity = opacity
shadowLayer.shadowRadius = radius
layer.insertSublayer(shadowLayer, at: 0)
}
}
# References
Advanced UIView shadow effects using shadowPath (opens new window)