Customize SF Symbols in SwiftUI and UIKit

This article walks through the practical ways to control SF Symbols across Apple's two common UI stacks. This article starts from the simple case of showing a symbol image, then moves through inline text usage, size and weight changes, whole-symbol tinting, hierarchical emphasis, multicolor rendering, and custom per-layer palettes.

Custom palette colors applied to layered SF Symbols

SF Symbols are more flexible than a single monochrome glyph. The useful part is knowing which rendering API matches the visual effect you want.

This article frames the topic as a practical customization guide for both SwiftUI and UIKit. It is not only about changing an icon's size. It is about choosing the right rendering mode for each case: plain symbol images, text embedding, font-driven scaling, hierarchical emphasis, single-tint coloring, Apple's preset multicolor symbols, and fully custom palette colors.

Tooling The article starts by pointing readers to Apple's SF Symbols app: developer.apple.com/sf-symbols.

The entry point is the same idea in both frameworks: load a symbol by name, then place it like any other image.

SwiftUI uses Image(systemName:):

Image(systemName: "swift")

The UIKit equivalent is just as direct:

let img = UIImage(systemName: "swift")
let imageView = UIImageView(image: img)
imageView.center = .init(
    x: view.bounds.width / 2,
    y: view.bounds.height / 2
)
view.addSubview(imageView)
SF Symbols app with a selected symbol visible
The SF Symbols app is the best place to inspect symbol names and available rendering styles before writing code.

When the icon should read as part of a sentence, SwiftUI and UIKit both support inline symbol insertion instead of forcing a separate image view.

The SwiftUI example is compact because Image can interpolate inside Text:

Text("You can put a symbol \(Image(systemName: "swift")) within a text.")

UIKit takes the attributed-string route with NSTextAttachment:

let imgAttachment = NSTextAttachment(
    image: UIImage(systemName: "swift")!
)
let labelText = NSMutableAttributedString(
    string: "Favorite programming language: "
)
labelText.append(
    NSMutableAttributedString(attachment: imgAttachment)
)
labelText.append(NSAttributedString(string: "Swift"))

let label = UILabel()
label.attributedText = labelText
label.center = .init(x: 0, y: view.bounds.height / 2)
label.sizeToFit()
view.addSubview(label)
Text with an SF Symbol embedded inline
This is the clean way to make a symbol feel like part of the sentence rather than a separate component.

Symbol size is tied to typography, so the most natural control surface is a text style or a font size.

In SwiftUI, you can use a preset text style or specify an exact size:

// A larger SF Symbol
Image(systemName: "swift")
    .font(.title)

// A specific symbol size
Image(systemName: "checkmark")
    .font(.system(size: 30))

UIKit exposes the same concept through UIImage.SymbolConfiguration:

let largeImg = UIImage(
    systemName: "swift",
    withConfiguration: UIImage.SymbolConfiguration(textStyle: .largeTitle)
)

let customFontSizeImg = UIImage(
    systemName: "swift",
    withConfiguration: UIImage.SymbolConfiguration(
        font: .systemFont(ofSize: 60)
    )
)
Several SF Symbols shown at different sizes
Presets are good for matching system typography. Fixed sizes are useful when the symbol is more decorative.

You can combine size and weight to make the symbol feel like it belongs to a specific typographic voice rather than only scaling it up.

The SwiftUI pattern is concise:

Image(systemName: "checkmark")
    .font(.title.weight(.black))
    .foregroundColor(.green)

UIKit builds that configuration in pieces and then applies them together:

let weightConfigBlack = UIImage.SymbolConfiguration(weight: .black)
let fontConfigLargeTitle = UIImage.SymbolConfiguration(textStyle: .largeTitle)
let largeBoldConfig = weightConfigBlack.applying(fontConfigLargeTitle)

let largeBlackImg = UIImage(
    systemName: "checkmark",
    withConfiguration: largeBoldConfig
)

This is the point where SF Symbols stop feeling like generic icons and start matching the typographic emphasis of the surrounding UI.

Some symbols have layered structure, and hierarchical rendering lets you emphasize the front layer instead of tinting every shape equally.

The SwiftUI comparison in this article uses the same symbol twice, with and without hierarchical rendering:

Section {
    Image(systemName: "checkmark.icloud.fill")
        .font(.largeTitle)
        .foregroundColor(.blue)

    Image(systemName: "checkmark.icloud.fill")
        .font(.largeTitle)
        .foregroundColor(.blue)
        .symbolRenderingMode(.hierarchical)
}

UIKit exposes the same effect through a hierarchical color configuration:

let frontHighlightedIcon = UIImage(
    systemName: "checkmark.icloud.fill",
    withConfiguration: UIImage.SymbolConfiguration(
        hierarchicalColor: .systemBlue
    )
)
Hierarchical rendering highlighting the front layer of an SF Symbol
Hierarchical rendering is useful when you want depth and emphasis without manually specifying a palette.

If you only want one color across the entire symbol, use a single tint instead of a layered rendering mode.

SwiftUI keeps that simple:

Image(systemName: "checkmark")
    .foregroundColor(.green)

UIKit uses the symbol image and applies a tint color:

let blueInternetImg = UIImage(systemName: "network")?
    .withTintColor(.systemBlue)
SF Symbols tinted with a single color
This is the right choice when the symbol is more like a standard icon accent than a layered illustration.

For the most expressive symbols, you have two options: use Apple's built-in multicolor design where available, or define your own palette colors per layer.

This article first points out that some symbols already carry Apple's own color recipe. In SwiftUI, you can request that preset with .multicolor:

Image(systemName: "checkmark.circle.trianglebadge.exclamationmark")
    .font(.largeTitle)
    .symbolRenderingMode(.multicolor)

UIKit has the equivalent convenience configuration:

let multiColorImg = UIImage(
    systemName: "gear.badge.checkmark",
    withConfiguration: UIImage.SymbolConfiguration.preferringMulticolor()
)
Result of a multicolor SF Symbol
Some symbols already ship with a multicolor look designed by Apple.
SF Symbols app showing the multicolor option
The SF Symbols app lets you preview whether a symbol supports the multicolor preset.

If you want full control, switch to palette rendering and specify each layer yourself:

Image(systemName: "display.trianglebadge.exclamationmark")
    .font(.largeTitle)
    .symbolRenderingMode(.palette)
    .foregroundStyle(.red, .green)

Image(systemName: "network.badge.shield.half.filled")
    .font(.largeTitle)
    .symbolRenderingMode(.palette)
    .foregroundStyle(.green, .blue)

UIKit does the same through paletteColors:

let multiColorImg = UIImage(
    systemName: "gear.badge.checkmark",
    withConfiguration: UIImage.SymbolConfiguration(
        paletteColors: [.systemGreen, .systemBlue]
    )
)
Custom palette colors applied to different SF Symbol layers
Palette rendering is the most explicit option because you choose the colors per layer instead of accepting a preset.

The important idea is not memorizing every rendering mode. It is recognizing which visual goal you are aiming for, then choosing the matching API.

Use the plain symbol initializer when you only need the icon. Use text interpolation or attachments for inline symbols. Use font-driven sizing and weight when the icon should match typography. Use hierarchical, multicolor, or palette rendering when the symbol has meaningful layers.

That is the practical path this article lays out for both SwiftUI and UIKit.