Swift
iOS Development
Drop Shadow
UILabel
SwiftUI

How to make a drop shadow effect on a label in Swift?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Creating a drop shadow effect on a label in Swift can significantly enhance the visual appeal of your iOS application. Shadows help create depth and make UI components appear elevated, which can be particularly effective in drawing user attention or enhancing readability. In this article, we will explore how to apply drop shadow effects to labels using both UIKit and SwiftUI, with detailed code examples and practical guidance.

How Shadows Work in iOS

Drop shadows simulate the effect of a light source casting a shadow behind an object. In UI design, shadows give a three-dimensional feel to otherwise flat elements. iOS provides comprehensive APIs that allow developers to customize shadows through properties like color, offset, radius, and opacity. Under the hood, the rendering engine draws an additional blurred shape behind the view based on these properties.

Adding a Drop Shadow to UILabel in UIKit

The most common approach uses the layer property of UILabel, which gives you access to Core Animation shadow properties.

Basic Shadow Implementation

swift
1import UIKit
2
3class ViewController: UIViewController {
4    override func viewDidLoad() {
5        super.viewDidLoad()
6
7        let label = UILabel()
8        label.text = "Hello, Shadow!"
9        label.font = UIFont.systemFont(ofSize: 24, weight: .bold)
10        label.textColor = .black
11        label.translatesAutoresizingMaskIntoConstraints = false
12
13        // Configure the shadow
14        label.layer.shadowColor = UIColor.black.cgColor
15        label.layer.shadowOpacity = 0.5
16        label.layer.shadowOffset = CGSize(width: 2, height: 2)
17        label.layer.shadowRadius = 4
18
19        view.addSubview(label)
20        NSLayoutConstraint.activate([
21            label.centerXAnchor.constraint(equalTo: view.centerXAnchor),
22            label.centerYAnchor.constraint(equalTo: view.centerYAnchor)
23        ])
24    }
25}

Here is what each shadow property controls:

  • shadowColor: The color of the shadow. You must use cgColor because this property expects a CGColor, not a UIColor.
  • shadowOpacity: A value from 0.0 (fully transparent) to 1.0 (fully opaque). Values around 0.3 to 0.6 tend to look natural.
  • shadowOffset: A CGSize controlling horizontal and vertical displacement. Positive width moves the shadow right, positive height moves it down.
  • shadowRadius: The blur radius. Higher values produce a softer, more diffuse shadow. Lower values create a sharper edge.

Optimizing Shadow Rendering with shadowPath

By default, UIKit calculates the shadow shape from the view's contents on every render pass. For labels and other rectangular views, you can dramatically improve performance by providing an explicit shadow path.

swift
1override func viewDidLayoutSubviews() {
2    super.viewDidLayoutSubviews()
3    label.layer.shadowPath = UIBezierPath(rect: label.bounds).cgPath
4}

Setting shadowPath tells Core Animation exactly what shape to use, which eliminates the need for expensive offscreen rendering. This is especially important when you have multiple shadowed labels in a scrolling list or table view.

Using NSShadow for More Control

If you need the shadow to follow the text shape precisely rather than the label bounds, you can use NSAttributedString with NSShadow.

swift
1let shadow = NSShadow()
2shadow.shadowColor = UIColor.gray
3shadow.shadowOffset = CGSize(width: 1, height: 1)
4shadow.shadowBlurRadius = 3
5
6let attributes: [NSAttributedString.Key: Any] = [
7    .shadow: shadow,
8    .font: UIFont.systemFont(ofSize: 24, weight: .bold),
9    .foregroundColor: UIColor.black
10]
11
12label.attributedText = NSAttributedString(string: "Shadow Text", attributes: attributes)

This approach applies the shadow directly to the text glyphs rather than the entire label layer. The visual difference is subtle but noticeable on labels with large padding or background colors.

Adding a Drop Shadow in SwiftUI

SwiftUI provides a built-in .shadow() modifier that makes adding shadows straightforward.

swift
1import SwiftUI
2
3struct ShadowLabelView: View {
4    var body: some View {
5        Text("Hello, Shadow!")
6            .font(.title)
7            .fontWeight(.bold)
8            .foregroundColor(.black)
9            .shadow(color: .black.opacity(0.4), radius: 4, x: 2, y: 2)
10    }
11}

The shadow modifier accepts color, radius, x offset, and y offset. You can also stack multiple shadow modifiers to create layered shadow effects for a more dramatic appearance.

swift
1Text("Layered Shadow")
2    .font(.title)
3    .shadow(color: .black.opacity(0.3), radius: 2, x: 1, y: 1)
4    .shadow(color: .black.opacity(0.15), radius: 8, x: 4, y: 4)

Common Pitfalls

  • Performance degradation: Shadows without an explicit shadowPath trigger offscreen rendering on every frame. If you notice choppy scrolling, set shadowPath or enable shouldRasterize on the layer.
  • Invisible shadows: If your shadow does not appear, check that shadowOpacity is greater than 0 and that clipsToBounds (or masksToBounds) is set to false. Clipping hides the shadow because it renders outside the view bounds.
  • Jagged edges on low-radius shadows: A shadowRadius of 0 or 1 can look pixelated. Pair low radius values with lower opacity for a cleaner result.
  • Simulator vs. device differences: Always verify shadow appearance on a real device. The iOS Simulator uses a different rendering pipeline that may display shadows with slightly different intensity or blur.

Summary

Adding drop shadows to labels in Swift is a simple but effective way to improve your app's visual depth. In UIKit, use layer.shadowColor, shadowOpacity, shadowOffset, and shadowRadius for quick setup, and set shadowPath for better performance. For text-specific shadows, NSShadow with attributed strings gives you finer control. In SwiftUI, the .shadow() modifier handles everything in a single line. Whichever approach you choose, keep performance in mind by avoiding unnecessary offscreen rendering, especially in scrollable views.


Course illustration
Course illustration

All Rights Reserved.