How to Use Custom Fonts with Dynamic Type in iOS

Use custom fonts with Dynamic Type in iOS by registering font files in Info.plist, loading them with UIFont(name:size:), and scaling them with UIFontMetrics.scaledFont(for:). UIFontMetrics bridges the gap between custom typefaces and Apple's Dynamic Type system. It takes a custom UIFont at a base size and scales it proportionally using the metrics of a specified UIFontTextStyle. Pair this with adjustsFontForContentSizeCategory on UILabel for automatic updates when the user changes their preferred reading size in Settings.

Add Font Files to the Xcode Project

Create a Fonts group in the Project Navigator. Drag TTF or OTF font files into the group. Check Copy files to destination and verify the target membership includes your app.

Xcode copies the font files into the app bundle at build time. Both TrueType (.ttf) and OpenType (.otf) formats work identically on iOS.

Register Fonts in Info.plist

Add the Fonts provided by application key to Info.plist. Add each font filename as an array item.

Fonts provided by application
    Item 0: Mohave-Italic.ttf
    Item 1: HalloSans.otf
    Item 2: Canter-Light.otf

The filename must match exactly, including capitalization and extension. A single typo in Info.plist is the most common cause of custom fonts failing to appear.

Verify Font Registration at Runtime

Print all registered font families and names to confirm correct installation.

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

The font name used in UIFont(name:size:) often differs from the filename. For example, Canter-Light.otf has the font name "Canter Light". This runtime listing shows the exact string required.

Use Custom Fonts with UIFont(name:size:)

Set the font directly on labels and other text elements.

titleLabel.font = UIFont(name: "Mohave-Italic", size: 28.0)
bodyLabel.font = UIFont(name: "Hallo sans", size: 17.0)
captionLabel.font = UIFont(name: "Canter Light", size: 12.0)

UIFont(name:size:) returns nil if the font name is incorrect. Always provide a fallback. Use nil-coalescing with UIFont.systemFont(ofSize:) or UIFont.preferredFont(forTextStyle:) as a safety net.

Apply Custom Fonts in Interface Builder

Select a label in the storyboard. Open the Attributes inspector. Click the Font dropdown and choose Custom. Select the font family and style from the picker. Interface Builder renders the font live on the canvas.

Interface Builder font selection works only after the fonts are registered in Info.plist and the project has been built at least once.

Enable Dynamic Type with UIFontMetrics

UIFontMetrics scales custom fonts proportionally across all accessibility content size categories.

guard let customFont = UIFont(name: "Mohave-Italic", size: 28.0) else { return }

let metrics = UIFontMetrics(forTextStyle: .title1)
titleLabel.font = metrics.scaledFont(for: customFont)
titleLabel.adjustsFontForContentSizeCategory = true

UIFontMetrics(forTextStyle:) loads the scaling curve for the specified text style. scaledFont(for:) returns a new UIFont at the appropriate size for the user's current content size category. adjustsFontForContentSizeCategory makes the label re-query the scaled font when the user changes their preferred text size.

Use System Text Styles as Reference Sizes

Apple provides default point sizes for each text style at the Large content size category.

.largeTitle: 34pt, .title1: 28pt, .title2: 22pt, .title3: 20pt, .headline: 17pt bold, .body: 17pt, .callout: 16pt, .subheadline: 15pt, .footnote: 13pt, .caption1: 12pt, .caption2: 11pt.

Use these as starting points for base font sizes. Adjust upward for condensed typefaces with small x-heights and downward for expansive typefaces.

Provide Fallback Fonts for Graceful Degradation

Custom font loading can fail silently. Always provide a system font fallback.

titleLabel.font = UIFont(name: "Mohave-Italic", size: 28.0)
    ?? UIFont.preferredFont(forTextStyle: .title1)
titleLabel.adjustsFontForContentSizeCategory = true

The nil-coalescing operator falls back to a Dynamic Type system font when the custom font is unavailable. This ensures the app remains readable even if a font file is missing.

Create a Centralized Font Style Guide

Define font constants in a single enum to avoid magic numbers scattered across view controllers.

enum AppFonts {
    static let title = UIFont(name: "Mohave-Italic", size: 28.0)
        ?? UIFont.preferredFont(forTextStyle: .title1)
    static let body = UIFont(name: "Hallo sans", size: 17.0)
        ?? UIFont.preferredFont(forTextStyle: .body)
    static let caption = UIFont(name: "Canter Light", size: 12.0)
        ?? UIFont.preferredFont(forTextStyle: .caption1)
}

A centralized style guide simplifies font changes across the app. Update one file instead of searching through every view controller.

Test Dynamic Type on Simulator and Device

Open Settings on the simulator or device. Navigate to Accessibility > Display & Text Size > Larger Text. Drag the slider to various positions. Return to the app and verify that text scales correctly.

Test at Accessibility Extra Extra Extra Large (the largest setting) to ensure text does not overflow its container. Test at Extra Small (the smallest setting) to ensure text remains legible. Use UIStackView with proper constraints to accommodate size changes without layout breaks.

Best Practices for Custom Fonts and Dynamic Type

Always include a system font fallback with nil-coalescing or a custom default. Define fonts in a centralized style enum for maintainability. Limit custom fonts to two or three families to maintain visual consistency. Verify font licenses for commercial use before shipping. Subset font files to include only the glyphs your app uses, reducing bundle size by 50 to 90 percent. Test on physical devices because simulator font rendering differs from real hardware. Use adjustsFontForContentSizeCategory on every label that uses a custom font with UIFontMetrics. Audit font file sizes and remove unused font weights to keep the app bundle lean.

Summary

This tutorial covered the complete implementation process from setup to working code. You now have a functional implementation that you can extend and customize for your own projects. The same patterns apply to related features in the iOS ecosystem.

Frequently Asked Questions

What Xcode version do I need for this tutorial? This tutorial uses Xcode 26 with Swift 6.2. The code examples work with any recent version of Xcode that supports the frameworks used here.

Can I test this on the iOS Simulator? Some features require a physical device with a compatible processor. The article notes specific sections where the Simulator is sufficient and where a real device is required.

What are the prerequisites for this tutorial? You need Xcode installed on a Mac and basic familiarity with Swift programming. No prior experience with the specific framework is required.

All articles