Dynamic Type: Key Concepts, Text Styles, and How It Works in iOS

Author: IT Sectr Published: 2026-05-16 Reading time: 9 min

Dynamic Type is a built-in iOS feature that automatically changes the font size in an app according to the user’s system settings. The user sets their preferred text size in Settings → Display & Brightness → Text Size, and all apps supporting Dynamic Type adapt without any additional actions. According to the Apple Human Interface Guidelines, 2024, supporting Dynamic Type is a mandatory requirement for publishing on the App Store, as it is a basic element of accessibility on the iOS platform.

Key Takeaways

  • Dynamic Type — an iOS mechanism for automatically scaling text according to system font size settings
  • Support is provided through text styles (UIFont.TextStyle) — body, headline, caption, title, and others
  • The system supports 11 levels of scaling from XSmall to AccessibilityXXXL
  • For custom fonts, the UIFontMetrics method is used, scaling any font relative to the body style
  • Layout must be adaptive — UIStackView, Auto Layout, and dynamic cell heights are mandatory

What is Dynamic Type in iOS

Dynamic Type is an iOS technology introduced in iOS 7 that allows text in an app to automatically adjust to the system font size settings. The user can increase or decrease the font in all apps at once — without having to change settings inside each individual app.

According to Apple WWDC 2023, “Make Your App Visually Accessible”, over 40% of iOS users change the font size from the default. For the 65+ age group, this figure reaches 70%. Dynamic Type is a mandatory requirement for App Store accessibility certification. Lack of support is a reason for app rejection during review.

How it works: the app uses UIFont.preferredFont(forTextStyle:) instead of UIFont(name:size:). iOS automatically returns a font scaled to the current system setting. When the size changes in settings, iOS generates a UIContentSizeCategory.didChangeNotification — the app must recalculate the layout.

History of Dynamic Type

Dynamic Type appeared in iOS 7 alongside the flat design. iOS 10 added support for custom fonts via UIFontMetrics. iOS 11 introduced accessibility levels (up to AccessibilityXXXL). iOS 15 added Dynamic Type in SwiftUI with automatic support. With each release, Apple tightens the requirements: starting with iOS 17, apps without Dynamic Type support receive a warning when uploading to App Store Connect.

UIFont.TextStyle Text Styles

iOS provides 8 text styles, each with a predefined size and weight for the default setting:

StyleConstantSize (L)Purpose
Large Title.largeTitle34pxMain screen title
Title 1.title128pxSection heading
Title 2.title222pxSubheading
Title 3.title320pxCard title
Headline.headline17px (bold)Bold for emphasis
Body.body17pxMain body text
Callout.callout16pxSupplementary text
Caption 1.caption112pxImage caption
Caption 2.caption211pxSmall caption
Footnote.footnote13pxFootnote, note

Using the correct style is not just about “appearance.” UIFont.TextStyle.body in AccessibilityXXXL mode can reach 53px. If an app uses a fixed 17px font for body, the text becomes unreadable for users who have increased the font size.

Visual Hierarchy Through Styles

Dynamic Type does not just scale — it preserves visual hierarchy. Large Title is always larger than Title 1, which is larger than Body, regardless of the scaling level. Scaling coefficients differ: headings scale more aggressively than body text, so that hierarchy is maintained even at accessibility levels.

Scaling Levels

iOS supports 11 levels of text scaling, divided into two categories:

  • Standard sizes (5 levels): XS, S, M, L (default), XL
  • Accessibility sizes (6 levels): accessibilityXL, accessibilityXXL, accessibilityXXXL, accessibilityXXXXL, accessibilityXXXXXL, accessibilityXXXXXXL

The difference between XS and AccessibilityXXXL for the body style ranges from 14px to 53px — almost 4×. A layout designed for 17px completely breaks at 53px: text overflows, buttons overlap, cells collide.

Check the current category in code:

swift
let category = UIApplication.shared.preferredContentSizeCategory
// .extraSmall, .small, .medium, .large, .extraLarge ...
if category.isAccessibilityCategory {
    // Enable alternative layout
}

traitCollection and Text Size

The text size category is available via traitCollection.uiContentSizeCategory. When the system setting changes, iOS calls traitCollectionDidChange on all UIViews. In this method, you need to update fonts and recalculate the layout. SwiftUI does this automatically — UIKit requires manual subscription.

For UICollectionView, use UICollectionViewCompositionalLayout — it automatically adjusts the number of columns to the screen width and text size. At accessibility levels, switch from a two-column to a single-column layout so that text is not truncated and elements do not overlap. Use conditional layout: for regular width — two columns, for compact or accessibility — one column.

Implementation in UIKit

Basic implementation in UIKit — UIFont.preferredFont(forTextStyle:). This method returns a font scaled to the current system setting:

swift
titleLabel.font = UIFont.preferredFont(forTextStyle: .headline)
bodyLabel.font = UIFont.preferredFont(forTextStyle: .body)

For custom fonts, use UIFontMetrics:

swift
let customFont = UIFont(name: "Montserrat-Regular", size: 16)!
titleLabel.font = UIFontMetrics(forTextStyle: .body)
    .scaledFont(for: customFont)

// Tracking size changes
NotificationCenter.default.addObserver(
    self,
    selector: #selector(preferredContentSizeChanged),
    name: UIContentSizeCategory.didChangeNotification,
    object: nil
)

For UILabel in Interface Builder, simply set the font as a text style and enable “Automatically Adjusts Font.” For custom fonts, Interface Builder does not support UIFontMetrics — only through code.

UIFontMetrics and Scaling Curve

UIFontMetrics uses the same scaling curve as preferredFont. You can specify fromTextStyle: .body, .headline, etc. Each style has its own scaling coefficient. Body scales moderately, Large Title scales aggressively. If your custom font is used for body text, use .body.

Dynamic Type in SwiftUI

In SwiftUI, Dynamic Type support is built in by default. All system modifiers (.font(.body), .font(.title)) automatically scale. The developer does not need to call UIFont.preferredFont.

Example:

swift
Text("Main text")
    .font(.body)
    .lineLimit(nil)
    .minimumScaleFactor(0.5)

For custom scales, use DynamicTypeSize:

swift
@Environment(\.dynamicTypeSize) var dynamicTypeSize

var body: some View {
    Text("Adaptive text")
        .font(.body)
        .padding(dynamicTypeSize <= .large ? 8 : 16)
}

SwiftUI automatically updates the view when the system text size changes — no additional notification subscriptions needed.

Limiting Dynamic Type in SwiftUI

The .font(.body) modifier only works for the system font. For custom fonts in SwiftUI, use Font.custom with UIFontMetrics under the hood. Starting with iOS 16, SwiftUI supports DynamicTypeSize in @Environment, allowing you to adapt padding and layout.

Adapting Layout for Large Fonts

Auto Layout is a prerequisite for Dynamic Type support. Fixed widths and heights (>=, <=) break at accessibility sizes. Use intrinsicContentSize for UILabel — it automatically calculates the height for the current font size.

Adaptive layout rules:

  • UIStackView with distribution = fill and spacing >= 8 — elements will wrap to the next line
  • UITableViewCell with automaticDimension — cell height adjusts to content
  • numberOfLines = 0 on UILabel — text is not truncated but wraps
  • Buttons with edgeInsets — add padding at accessibility levels via traitCollection.preferredContentSizeCategory

For long texts (e.g., articles, terms of use), set minimumScaleFactor on UILabel — this reduces the text as a last resort when Auto Layout cannot fit the content. A value of 0.5 means the font can shrink to 50% of the preferred size.

Example of adjusting padding to the size category:

swift
let isAccessibility = traitCollection
    .preferredContentSizeCategory.isAccessibilityCategory
stackView.spacing = isAccessibility ? 16 : 8
button.contentEdgeInsets = isAccessibility
    ? UIEdgeInsets(top: 16, left: 24, bottom: 16, right: 24)
    : UIEdgeInsets(top: 8, left: 16, bottom: 8, right: 16)

Typical Layout Issues at Accessibility Levels

Text truncation — UILabel with numberOfLines = 1 and fixed width truncates text at AccessibilityXL. Solution: numberOfLines = 0 and trailing constraint. Element overlap — buttons with fixed height overlap each other. Solution: UIStackView with automatic spacing. Content going off-screen — ScrollView is mandatory for accessibility levels.

Testing Dynamic Type

Xcode Simulator allows you to change the text size: in the simulator menu, go to Settings → Accessibility → Display & Text Size → Larger Text. Switch between all 11 levels and verify that text is not truncated, buttons are accessible, and the layout does not break.

For automated testing, use the contentSizeCategory setting in XCUIApplication:

swift
func testAccessibilitySizes() {
    let app = XCUIApplication()
    app.launchArguments += [
        "-UIPreferredContentSizeCategoryName",
        "UICTContentSizeCategoryAccessibilityXXXL"
    ]
    app.launch()
    app.scrollViews.buttons["Submit"].tap()
    XCTAssertTrue(app.staticTexts["Form submitted"]
        .waitForExistence(timeout: 5))
}

XCUITest with different launchArguments allows you to check all scaling levels in CI. Be sure to test accessibility categories separately — they most often break layouts. According to Apple, 70% of Dynamic Type bugs are found specifically at accessibility levels, because developers only test standard sizes.

Accessibility Inspector: Checking Dynamic Type

Xcode Accessibility Inspector shows which fonts the selected element uses — fixed or scalable. If the element does not respond to text size changes in the simulator, a fixed font is being used. Replace it with UIFont.preferredFont or UIFontMetrics.

Frequently Asked Questions

What is Dynamic Type in iOS in simple terms?

It is automatic text scaling in an app according to system size settings. The user changes the size in Settings — all supporting apps adapt without developer involvement.

Does Dynamic Type only work with system fonts?

No. For custom fonts, use UIFontMetrics(scaledFont:). This method scales any font relative to the specified text style, preserving proportions.

How many scaling levels does iOS support?

11 levels: 5 standard (XS, S, M, L, XL) and 6 accessibility levels (from AccessibilityXL to AccessibilityXXXXXL). The body font size can range from 14px to 53px.

How to subscribe to text size changes?

In UIKit via UIContentSizeCategory.didChangeNotification. In SwiftUI, use @Environment(\.dynamicTypeSize) — the view updates automatically when the system setting changes.

What happens if you don’t support Dynamic Type?

Users with increased font size will see truncated text, overlapping elements, and broken buttons. The app may be rejected on App Store Review for violating accessibility requirements.

Summary

  • Dynamic Type — a built-in iOS mechanism for scaling text to system settings; mandatory for App Store
  • Use UIFont.preferredFont(forTextStyle:) for system fonts and UIFontMetrics for custom ones
  • iOS supports 11 levels of scaling — from XS to AccessibilityXXXXXL (14–53px for body)
  • In SwiftUI, Dynamic Type works automatically via .font(.body) and @Environment(\.dynamicTypeSize)
  • Layout must be adaptive: UIStackView, automaticDimension in tables, numberOfLines = 0
  • Test all 11 levels, especially accessibility categories — 70% of layouts break on them
  • Subscription to UIContentSizeCategory.didChangeNotification is mandatory for UIKit apps

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also