BS
BleepingSwift
Published on

> Using Custom Fonts in SwiftUI

Authors
  • avatar
    Name
    Mick MacCallum
    Twitter
    @0x7fs

Fonts set the tone for your app. Whether you're using San Francisco's built-in variants or bringing in a custom typeface, SwiftUI makes typography straightforward once you understand the options. The tricky part is ensuring your fonts scale properly with Dynamic Type so users who need larger text aren't left behind.

System Fonts

For most apps, the system font is the right choice. It's optimized for readability on Apple devices and automatically adapts to the user's accessibility settings. SwiftUI provides semantic text styles that map to appropriate sizes:

Text("Large Title").font(.largeTitle)
Text("Title").font(.title)
Text("Headline").font(.headline)
Text("Body").font(.body)
Text("Caption").font(.caption)

These styles aren't just different sizes—they're designed to work together hierarchically. Using them consistently gives your app a polished feel without manual sizing decisions.

You can modify system fonts with weight, width, and design variants:

Text("Bold Body")
    .font(.body.weight(.bold))

Text("Monospaced")
    .font(.body.monospaced())

Text("Rounded")
    .font(.system(.body, design: .rounded))

Text("Serif")
    .font(.system(.body, design: .serif))

The design parameter gives you access to SF Pro Rounded, New York (serif), and SF Mono without importing any custom fonts.

Adding Custom Fonts

To use a custom font, you'll need to add the font files to your project and register them in your Info.plist.

First, drag your font files (.ttf or .otf) into your Xcode project. Make sure "Copy items if needed" is checked and the files are added to your app target.

Then add the fonts to your Info.plist under the key Fonts provided by application (or UIAppFonts in the raw key form):

<key>UIAppFonts</key>
<array>
    <string>Lato-Regular.ttf</string>
    <string>Lato-Bold.ttf</string>
</array>

Now you can use the font by its PostScript name:

Text("Custom Font")
    .font(.custom("Lato-Regular", size: 17))

If you're unsure of the PostScript name, you can print all available fonts at runtime:

for family in UIFont.familyNames.sorted() {
    print("Family: \(family)")
    for name in UIFont.fontNames(forFamilyName: family) {
        print("  \(name)")
    }
}

Dynamic Type with Custom Fonts

Here's where many developers stumble. Using .font(.custom("Lato-Regular", size: 17)) gives you a fixed 17-point font that ignores the user's Dynamic Type preferences. Users who've set their system to use larger text will see your custom font at the same small size, which can be a serious accessibility issue.

To make custom fonts scale with Dynamic Type, use the relativeTo parameter:

Text("Scales with Body")
    .font(.custom("Lato-Regular", size: 17, relativeTo: .body))

Text("Scales with Title")
    .font(.custom("Lato-Bold", size: 28, relativeTo: .title))

The size you specify becomes the base size at the default text size setting. When users increase their text size, SwiftUI scales your font proportionally based on how the referenced text style scales.

For complete control, you can use a fixed size that explicitly opts out of scaling—but only when you have a specific reason:

Text("Fixed Size")
    .font(.custom("Lato-Regular", fixedSize: 12))

Use this sparingly. Tab bar labels or very constrained UI elements might need fixed sizes, but body text should always scale.

Creating a Font Extension

Rather than scattering font names throughout your codebase, create an extension that centralizes your typography:

extension Font {
    static func appBody() -> Font {
        .custom("Lato-Regular", size: 17, relativeTo: .body)
    }

    static func appHeadline() -> Font {
        .custom("Lato-Bold", size: 17, relativeTo: .headline)
    }

    static func appTitle() -> Font {
        .custom("Lato-Bold", size: 28, relativeTo: .title)
    }
}

// Usage
Text("Hello")
    .font(.appBody())

This makes it easy to update your typography across the entire app and keeps your views clean.

Troubleshooting

If your custom font isn't appearing, check these common issues:

The font file might not be included in your target. Select the font file in Xcode's project navigator and verify that your app target is checked in the File Inspector's Target Membership section.

The font name in code might not match the PostScript name. Font file names and PostScript names are often different. "Lato-Regular.ttf" might have the PostScript name "Lato-Regular" or just "Lato"—use the font enumeration code above to find the exact name.

The Info.plist entry might be missing or misspelled. Double-check that the filename in UIAppFonts exactly matches your font file, including the extension.

System Font Features

Before reaching for a custom font, consider whether the system font variants cover your needs. San Francisco offers several features many developers don't know about:

// Small caps
Text("Small Caps")
    .font(.body.smallCaps())

// Lowercase small caps
Text("Lowercase Small Caps")
    .font(.body.lowercaseSmallCaps())

// Monospaced digits (useful for timers, prices)
Text("$1,234.56")
    .monospacedDigit()

// Leading-specific variants
Text("Tight leading")
    .font(.body.leading(.tight))

These features work automatically with Dynamic Type and don't require any additional setup.

Conclusion

The system font handles most cases well, and its built-in variants give you more flexibility than you might expect. When you do need a custom font, remember to use relativeTo to maintain Dynamic Type support. Your users with accessibility needs will thank you, and your app will feel more polished as a result.

subscribe.sh

// Stay Updated

Get notified when I publish new tutorials on Swift, SwiftUI, and iOS development. No spam, unsubscribe anytime.

>

By subscribing, you agree to our Privacy Policy.