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 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.
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.
iOS provides 8 text styles, each with a predefined size and weight for the default setting:
| Style | Constant | Size (L) | Purpose |
|---|---|---|---|
| Large Title | .largeTitle | 34px | Main screen title |
| Title 1 | .title1 | 28px | Section heading |
| Title 2 | .title2 | 22px | Subheading |
| Title 3 | .title3 | 20px | Card title |
| Headline | .headline | 17px (bold) | Bold for emphasis |
| Body | .body | 17px | Main body text |
| Callout | .callout | 16px | Supplementary text |
| Caption 1 | .caption1 | 12px | Image caption |
| Caption 2 | .caption2 | 11px | Small caption |
| Footnote | .footnote | 13px | Footnote, 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.
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.
iOS supports 11 levels of text scaling, divided into two categories:
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:
let category = UIApplication.shared.preferredContentSizeCategory
// .extraSmall, .small, .medium, .large, .extraLarge ...
if category.isAccessibilityCategory {
// Enable alternative layout
}
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.
Basic implementation in UIKit — UIFont.preferredFont(forTextStyle:). This method returns a font scaled to the current system setting:
titleLabel.font = UIFont.preferredFont(forTextStyle: .headline)
bodyLabel.font = UIFont.preferredFont(forTextStyle: .body)
For custom fonts, use UIFontMetrics:
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 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.
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:
Text("Main text")
.font(.body)
.lineLimit(nil)
.minimumScaleFactor(0.5)
For custom scales, use DynamicTypeSize:
@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.
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.
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:
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:
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)
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.
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:
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.
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
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.
No. For custom fonts, use UIFontMetrics(scaledFont:). This method scales any font relative to the specified text style, preserving proportions.
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.
In UIKit via UIContentSizeCategory.didChangeNotification. In SwiftUI, use @Environment(\.dynamicTypeSize) — the view updates automatically when the system setting changes.
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
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.
Read also