Dynamic Type: ключови понятия, текстови стилове и как работи в iOS

Автор: IT Sectr Публикувано: 2026-05-16 Време за четене: 9 мин

Dynamic Type — встроенная функция iOS, которая автоматически изменяет размер шрифта в приложении в соответствии с системными настройками пользователя. Пользователь задаёт предпочитаемый размер текста в Настройках → Экран и яркость → Размер текста, и все поддерживающие Dynamic Type приложения адаптируются без дополнительных действий. По данным Apple Human Interface Guidelines, 2024, поддержка Dynamic Type — обязательное требование для публикации в App Store, так как это базовый элемент accessibility на платформе iOS.

Главное

  • Dynamic Type — механизм iOS для автоматического масштабирования текста под системные настройки размера шрифта
  • Поддержка обеспечивается через текстовые стили (UIFont.TextStyle) — body, headline, caption, title и другие
  • Система поддерживает 11 уровней масштабирования от XSmall до AccessibilityXXXL
  • Для кастомных шрифтов используется метод UIFontMetrics, масштабирующий любой шрифт относительно стиля body
  • Вёрстка должна быть адаптивной — UIStackView, Auto Layout и динамическая высота ячеек обязательны

Что такое Dynamic Type в iOS

Dynamic Type — технология iOS, появившаяся в iOS 7, которая позволяет тексту в приложении автоматически подстраиваться под системные настройки размера шрифта. Пользователь может увеличить или уменьшить шрифт во всех приложениях сразу — без необходимости менять настройки внутри каждого приложения отдельно.

По данным Apple WWDC 2023, «Make Your App Visually Accessible», более 40% пользователей iOS изменяют размер шрифта от стандартного. Для возрастной группы 65+ этот показатель достигает 70%. Dynamic Type — обязательное требование для accessibility-сертификации App Store. Отсутствие поддержки — причина отклонения приложения на ревью.

Принцип работы: приложение использует UIFont.preferredFont(forTextStyle:) вместо UIFont(name:size:). iOS автоматически возвращает шрифт, масштабированный под текущую системную настройку. При изменении размера в настройках iOS генерирует уведомление UIContentSizeCategory.didChangeNotification — приложение должно пересчитать вёрстку.

История развития Dynamic Type

Dynamic Type появился в iOS 7 вместе с «плоским» дизайном. В iOS 10 добавили поддержку кастомных шрифтов через UIFontMetrics. iOS 11 ввёл accessibility-уровни (до AccessibilityXXXL). iOS 15 добавил Dynamic Type в SwiftUI с автоматической поддержкой. С каждым релизом Apple усиливает требования: начиная с iOS 17, приложения без поддержки Dynamic Type получают предупреждение при загрузке в App Store Connect.

Текстовые стили UIFont.TextStyle

iOS предоставляет 8 текстовых стилей, каждый с предопределённым размером и начертанием для стандартной настройки:

СтильКонстантаРазмер (L)Назначение
Large Title.largeTitle34pxГлавный заголовок экрана
Title 1.title128pxЗаголовок секции
Title 2.title222pxПодзаголовок
Title 3.title320pxЗаголовок карточки
Headline.headline17px (bold)Полужирный для акцентов
Body.body17pxОсновной текст
Callout.callout16pxВспомогательный текст
Caption 1.caption112pxПодпись под изображением
Caption 2.caption211pxМелкая подпись
Footnote.footnote13pxСноска, примечание

Использование правильного стиля — не просто «внешний вид». UIFont.TextStyle.body в режиме AccessibilityXXXL может достигать 53px. Если приложение использует фиксированный шрифт 17px для body, текст станет нечитаемым для пользователей, увеличивших шрифт.

Визуальная иерархия через стили

Dynamic Type не только масштабирует — он сохраняет визуальную иерархию. Large Title всегда крупнее Title 1, который крупнее Body, независимо от уровня масштабирования. Коэффициенты масштабирования различаются: заголовки масштабируются активнее, чем основной текст, чтобы иерархия сохранялась даже на accessibility-уровнях.

Уровни масштабирования

iOS поддерживает 11 уровней масштабирования текста, разделённых на две категории:

  • Standard sizes (5 уровней): XS, S, M, L (по умолчанию), XL
  • Accessibility sizes (6 уровней): accessibilityXL, accessibilityXXL, accessibilityXXXL, accessibilityXXXXL, accessibilityXXXXXL, accessibilityXXXXXXL

Разница между XS и AccessibilityXXXL для стиля body составляет от 14px до 53px — почти 4×. Вёрстка, рассчитанная на 17px, полностью ломается на 53px: текст выходит за границы, кнопки перекрываются, ячейки наезжают друг на друга.

Проверить текущую категорию в коде:

swift
let category = UIApplication.shared.preferredContentSizeCategory
// .extraSmall, .small, .medium, .large, .extraLarge ...
if category.isAccessibilityCategory {
    // Активиране на алтернативно оформление
}

traitCollection и размер текста

Категория размера текста доступна через traitCollection.uiContentSizeCategory. При изменении системной настройки iOS вызывает traitCollectionDidChange на всех UIView. В этом методе нужно обновить шрифты и пересчитать вёрстку. SwiftUI делает это автоматически — UIKit требует ручной подписки.

Для UICollectionView используйте UICollectionViewCompositionalLayout — он автоматически подстраивает количество колонок под ширину экрана и размер текста. На accessibility-уровнях переключайтесь с двухколоночного на одноколоночный макет, чтобы текст не обрезался и элементы не наезжали друг на друга. Используйте conditional layout: для regular width — две колонки, для compact или accessibility — одна колонка.

Реализация в UIKit

Базовая реализация в UIKit — UIFont.preferredFont(forTextStyle:). Этот метод возвращает шрифт, масштабированный под текущую системную настройку:

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

Для кастомных шрифтов используйте UIFontMetrics:

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

// Проследяване на промените в размера
NotificationCenter.default.addObserver(
    self,
    selector: #selector(preferredContentSizeChanged),
    name: UIContentSizeCategory.didChangeNotification,
    object: nil
)

Для UILabel в Interface Builder достаточно установить шрифт как текстовый стиль (Text Style) и включить «Automatically Adjusts Font». Для кастомных шрифтов IB не поддерживает UIFontMetrics — только через код.

UIFontMetrics и scaling curve

UIFontMetrics использует ту же scaling curve, что и preferredFont. Вы можете задать fromTextStyle: .body, .headline и т.д. Каждый стиль имеет свой коэффициент масштабирования. Body масштабируется умеренно, Large Title — агрессивно. Если ваш кастомный шрифт используется для основного текста, используйте .body.

Dynamic Type в SwiftUI

В SwiftUI поддержка Dynamic Type встроена по умолчанию. Все системные модификаторы (.font(.body), .font(.title)) автоматически масштабируются. Разработчику не нужно вызывать UIFont.preferredFont.

Пример:

swift
Text("Основен текст")
    .font(.body)
    .lineLimit(nil)
    .minimumScaleFactor(0.5)

Для кастомных масштабов используйте DynamicTypeSize:

swift
@Environment(\.dynamicTypeSize) var dynamicTypeSize

var body: some View {
    Text("Адаптивен текст")
        .font(.body)
        .padding(dynamicTypeSize <= .large ? 8 : 16)
}

SwiftUI автоматически обновляет представление при изменении системного размера текста — никаких дополнительных подписок на уведомления.

Ограничение Dynamic Type в SwiftUI

Модификатор .font(.body) работает только для системного шрифта. Для кастомных шрифтов в SwiftUI используйте Font.custom с UIFontMetrics под капотом. Начиная с iOS 16, SwiftUI поддерживает DynamicTypeSize в @Environment, что позволяет адаптировать отступы и компоновку.

Адаптация вёрстки под крупные шрифты

Auto Layout — обязательное условие поддержки Dynamic Type. Фиксированные ширины и высоты (>=, <=) ломаются при accessibility-размерах. Используйте intrinsicContentSize для UILabel — он автоматически вычисляет высоту под текущий размер шрифта.

Правила адаптивной вёрстки:

  • UIStackView с distribution = fill и spacing = >= 8 — элементы будут переноситься на следующую строку
  • UITableViewCell с automaticDimension — высота ячейки подстраивается под контент
  • numberOfLines = 0 на UILabel — текст не обрезается, а переносится
  • Кнопки с edgeInsets — добавьте отступы на accessibility-уровнях через traitCollection.preferredContentSizeCategory

Для длинных текстов (например, статьи, условия использования) установите minimumScaleFactor на UILabel — это уменьшает текст в последнюю очередь, когда Auto Layout не может вместить контент. Значение 0.5 означает, что шрифт может уменьшиться до 50% от предпочтительного размера.

Пример подстройки отступов под категорию размера:

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)

Типовые проблемы вёрстки на accessibility-уровнях

Обрезание текста — UILabel с numberOfLines = 1 и фиксированной шириной обрезает текст на AccessibilityXL. Решение: numberOfLines = 0 и trailing constraint. Перекрытие элементов — кнопки с фиксированной высотой наезжают друг на друга. Решение: UIStackView с automatic spacing. Выход за границы экрана — ScrollView обязателен для accessibility-уровней.

Тестирование Dynamic Type

Симулятор Xcode позволяет сменить размер текста: в меню симулятора Settings → Accessibility → Display & Text Size → Larger Text. Переключайте между всеми 11 уровнями и проверяйте, что текст не обрезается, кнопки доступны, вёрстка не ломается.

Для автоматизированного тестирования используйте настройку contentSizeCategory в XCUIApplication:

swift
func testAccessibilitySizes() {
    let app = XCUIApplication()
    app.launchArguments += [
        "-UIPreferredContentSizeCategoryName",
        "UICTContentSizeCategoryAccessibilityXXXL"
    ]
    app.launch()
    app.scrollViews.buttons["Изпращане"].tap()
    XCTAssertTrue(app.staticTexts["Формата е изпратена"]
        .waitForExistence(timeout: 5))
}

XCUITest с разными launchArguments позволяет проверить все уровни масштабирования в CI. Не забудьте проверить accessibility-категории отдельно — они чаще всего ломают вёрстку. По данным Apple, 70% багов Dynamic Type обнаруживаются именно на accessibility-уровнях, потому что разработчики тестируют только стандартные размеры.

Accessibility Inspector: проверка Dynamic Type

Xcode Accessibility Inspector показывает, какие шрифты использует выбранный элемент — фиксированный или масштабируемый. Если элемент не реагирует на изменение размера текста в симуляторе, значит используется фиксированный шрифт. Замените на UIFont.preferredFont или UIFontMetrics.

Часто задаваемые вопросы

Что такое Dynamic Type в iOS простыми словами?

Это автоматическое масштабирование текста в приложении под системные настройки размера. Пользователь меняет размер в Настройках — все поддерживающие приложения подстраиваются без участия разработчика.

Dynamic Type работает только с системными шрифтами?

Нет. Для кастомных шрифтов используйте UIFontMetrics(scaledFont:). Метод масштабирует любой шрифт относительно указанного текстового стиля, сохраняя пропорции.

Сколько уровней масштабирования поддерживает iOS?

11 уровней: 5 стандартных (XS, S, M, L, XL) и 6 accessibility-уровней (от AccessibilityXL до AccessibilityXXXXXL). Размер шрифта body может варьироваться от 14px до 53px.

Как подписаться на изменения размера текста?

В UIKit через UIContentSizeCategory.didChangeNotification. В SwiftUI используйте @Environment(\.dynamicTypeSize) — представление обновится автоматически при изменении системной настройки.

Что будет, если не поддерживать Dynamic Type?

Пользователи с увеличенным шрифтом увидят обрезанный текст, наезжающие элементы и неработающие кнопки. Приложение может быть отклонено на App Store Review за нарушение accessibility-требований.

Итоги

  • Dynamic Type — встроенный механизм iOS для масштабирования текста под системные настройки; обязателен для App Store
  • Используйте UIFont.preferredFont(forTextStyle:) для системных шрифтов и UIFontMetrics для кастомных
  • iOS поддерживает 11 уровней масштабирования — от XS до AccessibilityXXXXXL (14–53px для body)
  • В SwiftUI Dynamic Type работает автоматически через .font(.body) и @Environment(\.dynamicTypeSize)
  • Вёрстка обязана быть адаптивной: UIStackView, automaticDimension в таблицах, numberOfLines = 0
  • Тестируйте все 11 уровней, особенно accessibility-категории — на них ломается 70% вёрстки
  • Подписка на UIContentSizeCategory.didChangeNotification обязательна для UIKit-приложений

Ще разработим мобилно приложение под ключ

IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.

Обсъдете проекта

Прочетете също