Реалізація підтримки Dynamic Type (масштабування шрифтів) в iOS
Dynamic Type — механізм iOS, що дозволяє користувачу встановити бажаний розмір тексту в Settings → Accessibility → Display & Text Size → Larger Text. Діапазон: від xSmall до xxxLarge (7 стандартних + 5 додаткових «accessibility» розмірів через слайдер Larger Text з увімкненими Accessibility Sizes). Найбільший розмір — AX5 — збільшує шрифт приблизно в 3 рази від базового.
Якщо додаток не підтримує Dynamic Type, користувачі зі слабким зором вынуждені користуватися Zoom (масштабування всього екрану) — гірший UX. Підтримка Dynamic Type — перший та найважливіший крок доступності.
Як правильно підключити
UIKit
UIFont.preferredFont(forTextStyle: .body) — шрифт з підтримкою Dynamic Type з системної таблиці. Автоматично масштабується при зміні налаштування. adjustsFontForContentSizeCategory = true на UILabel, UITextField, UITextView — включає реагування на зміну в реальному часі (без перезапуску додатка).
Для кастомних шрифтів: UIFontMetrics(forTextStyle: .body).scaledFont(for: customFont). UIFontMetrics масштабує кастомний шрифт пропорційно системній таблиці для вибраного TextStyle.
Часта помилка: встановити шрифт через UIFont(name: "CustomFont-Regular", size: 17) без UIFontMetrics — розмір фіксований, Dynamic Type не працює.
SwiftUI
Font.body, Font.headline, Font.caption — системні шрифти з Dynamic Type з коробки. Для кастомних: Font.custom("CustomFont-Regular", size: 17, relativeTo: .body) — параметр relativeTo включає масштабування.
@ScaledMetric — для масштабування нешрифтових значень (відступи, іконки):
@ScaledMetric(relativeTo: .body) var iconSize: CGFloat = 24
При AX5 іконка виросте до ~72pt. Це важливо: іконки та відступи мають масштабуватися разом з текстом, інакше layout розваливається.
Де розваливається layout при великих розмірах
Фіксована висота рядків та контейнерів. UILabel з numberOfLines = 1 та фіксованою висотою — текст обрізається. На AX5 заголовок з 20 символів займає 3 рядки. Рішення: убрати фіксовані height constraints на текстових елементах, використовувати numberOfLines = 0 де можливо.
Горизонтальні стеки з текстом. UIStackView з axis = .horizontal та двома UILabel — при великому шрифті мітки не помістяться рядом. Потрібно або переключати axis на .vertical при великих розмірах через traitCollectionDidChange, або спочатку проектувати вертикальний layout.
У SwiftUI: ViewThatFits (iOS 16+) — вибирає layout залежно від доступного простору. Для ранніх версій — @Environment(\.sizeCategory) + умовний HStack/VStack.
Таблиці та колекції. UITableViewCell з Auto Layout та без фіксованої висоти — нормально. З tableView.rowHeight = 44 — при AX5 контент не влізе. tableView.rowHeight = UITableView.automaticDimension обов'язковий.
Кнопки з іконкою та текстом. На AX3+ текст може переноситися та кнопка змінює розмір — може порушити layout сусідніх елементів.
Тестування
Xcode Simulator: мінюємо розмір шрифту через Settings прямо в симуляторі. Або через Environment Overrides (Xcode Debug → Simulate Dynamic Type) — зручніше при розробці.
Обов'язково перевіряємо accessibility розміри (AX1-AX5) — багато команд тестують лише стандартний діапазон та пропускають проблеми з екстремальними розмірами.
Snapshot-тесты з фіксованими ContentSizeCategory: UITraitCollection(preferredContentSizeCategory: .accessibilityExtraExtraExtraLarge) — добавляємо в CI, щоб layout-регресії ловилися автоматично.
Термін: 2-3 дні для типового додатка. Основна частина роботи — правка layout constraints та кастомних компонентів. Вартість розраховується після аудиту.







