FREE: Start my 100 Days of SwiftUI today! >>

How to render shadows using NSShadow and setShadow()

Swift version: 5.2

Paul Hudson    @twostraws   

There are two ways to add shadows when rendering images: calling setShadow() and providing offset, blur, and color, or by using an NSShadow attached to an attributed string. Both have their own advantages, so both are worth trying.

First, here’s some example drawing code without a shadow:

let rect = CGRect(x: 0, y: 0, width: 512, height: 256)
let renderer = UIGraphicsImageRenderer(bounds: rect)

let img = renderer.image { ctx in
    UIColor.black.set()
    ctx.fill(rect)

    let str = """
    He thrusts his fists
    Against the posts
    And still insists
    He sees the ghosts
    """

    let attrs: [NSAttributedString.Key: Any] = [
        .font: UIFont.systemFont(ofSize: 36),
        .foregroundColor: UIColor.white
    ]

    let attributedString = NSAttributedString(string: str, attributes: attrs)
    attributedString.draw(in: rect.insetBy(dx: 50, dy: 50))
}

That draws some white text on a black background.

If we want to add a shadow effect to the text, we can use the setShadow method of the Core Graphics context we’re working with. For example, if you place this line before the draw() call at the end, you’ll make the text have a 5-point red glow:

ctx.cgContext.setShadow(offset: .zero, blur: 5, color: UIColor.red.cgColor)

The advantage of using setShadow() is that once you enable a shadow color, everything you draw has the same color – all text, all images, and all shapes.

When you’re done with the shadow and want normal rendering to resume, just use nil for the color value like this:

ctx.cgContext.setShadow(offset: .zero, blur: 0, color: nil)

The other way of drawing shadows is using NSAttributedString and the NSShadow class. This is an object you create and can attach to any attributed strings you want, giving you the flexibility to add shadowing to only certain parts of a string rather than the whole thing – something that setShadow() can’t do.

First, create an NSShadow instance like this:

let shadow = NSShadow()
shadow.shadowColor = UIColor.red
shadow.shadowBlurRadius = 5

That will create the same 5-point red glow as our earlier call to setShadow().

Now go ahead and put that into your attributed string dictionary using the .shadow key, like this:

let attrs: [NSAttributedString.Key: Any] = [
    .font: UIFont.systemFont(ofSize: 36),
    .foregroundColor: UIColor.white,
    .shadow: shadow
]

Here the end result will look identical to NSShadow, but as I said you now have the ability to shadow only parts of a string - or even add different shadows across the string.

Pro-tip: If you want to make your shadow stronger – to make it darker so that the color shows through more clearly – just draw your object repeatedly. For example, this will draw our attributed string five times to give it a really strong red glow:

for _ in 1...5 {
    attributedString.draw(in: rect.insetBy(dx: 50, dy: 50))
}
Hacking with Swift is sponsored by Bitrise

SPONSORED Build better iOS apps, faster. Looking for a great mobile CI/CD solution that has tons of iOS-specific tools, smooth code signing, and even real device testing? Learn more about Bitrise’s iOS-specific solutions.

Sign up for a free trial!

Sponsor Hacking with Swift and reach the world's largest Swift community!

Listen to the Swift over Coffee podcast

Available from iOS 10.0

Similar solutions…

About the Swift Knowledge Base

This is part of the Swift Knowledge Base, a free, searchable collection of solutions for common iOS questions.

BUY OUR BOOKS
Buy Pro Swift Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Coding Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift (Vapor Edition) Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Advanced iOS Volume Three Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Server-Side Swift (Kitura Edition) Buy Beyond Code

Was this page useful? Let us know!

Average rating: 2.0/5