Dynamic Type — вбудована функція iOS, яка автоматично змінює розмір шрифту в додатку відповідно до системних налаштувань користувача. Користувач задає бажаний розмір тексту в Налаштуваннях → Екран та яскравість → Розмір тексту, і всі додатки, що підтримують Dynamic Type, адаптуються без додаткових дій. Згідно з Apple Human Interface Guidelines, 2024, підтримка Dynamic Type — обов’язкова вимога для публікації в App Store, оскільки це базовий елемент accessibility на платформі 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 з’явився в iOS 7 разом з «плоским» дизайном. iOS 10 додав підтримку кастомних шрифтів через UIFontMetrics. iOS 11 ввів accessibility-рівні (до AccessibilityXXXL). iOS 15 додав Dynamic Type в SwiftUI з автоматичною підтримкою. З кожним релізом Apple посилює вимоги: починаючи з iOS 17, додатки без підтримки Dynamic Type отримують попередження при завантаженні в App Store Connect.
iOS надає 8 текстових стилів, кожен з попередньо визначеним розміром та накресленням для стандартного налаштування:
| Стиль | Константа | Розмір (L) | Призначення |
|---|---|---|---|
| Large Title | .largeTitle | 34px | Головний заголовок екрану |
| Title 1 | .title1 | 28px | Заголовок секції |
| Title 2 | .title2 | 22px | Підзаголовок |
| Title 3 | .title3 | 20px | Заголовок картки |
| Headline | .headline | 17px (напівжирний) | Напівжирний для акцентів |
| Body | .body | 17px | Основний текст |
| Callout | .callout | 16px | Допоміжний текст |
| Caption 1 | .caption1 | 12px | Підпис під зображенням |
| Caption 2 | .caption2 | 11px | Дрібний підпис |
| Footnote | .footnote | 13px | Виноска, примітка |
Використання правильного стилю — не просто «зовнішній вигляд». UIFont.TextStyle.body в режимі AccessibilityXXXL може досягати 53px. Якщо додаток використовує фіксований шрифт 17px для body, текст стане нечитабним для користувачів, які збільшили шрифт.
Dynamic Type не лише масштабує — він зберігає візуальну ієрархію. Large Title завжди більший за Title 1, який більший за Body, незалежно від рівня масштабування. Коефіцієнти масштабування різні: заголовки масштабуються активніше, ніж основний текст, щоб ієрархія зберігалася навіть на accessibility-рівнях.
iOS підтримує 11 рівнів масштабування тексту, розділених на дві категорії:
Різниця між XS та AccessibilityXXXL для стилю body становить від 14px до 53px — майже 4×. Верстка, розрахована на 17px, повністю ламається на 53px: текст виходить за межі, кнопки перекриваються, комірки наїзжджають одна на одну.
Перевірити поточну категорію в коді:
let category = UIApplication.shared.preferredContentSizeCategory
// .extraSmall, .small, .medium, .large, .extraLarge ...
if category.isAccessibilityCategory {
// Увімкнути альтернативну верстку
}
Категорія розміру тексту доступна через traitCollection.uiContentSizeCategory. При зміні системного налаштування iOS викликає traitCollectionDidChange на всіх UIView. У цьому методі потрібно оновити шрифти та перерахувати верстку. SwiftUI робить це автоматично — UIKit потребує ручної підписки.
Для UICollectionView використовуйте UICollectionViewCompositionalLayout — він автоматично підбудовує кількість колонок під ширину екрану та розмір тексту. На accessibility-рівнях перемикайтеся з двоколонної на одноколонну верстку, щоб текст не обрізався та елементи не наїзджали один на одного. Використовуйте умовну верстку: для regular width — дві колонки, для compact або accessibility — одна колонка.
Базова реалізація в UIKit — UIFont.preferredFont(forTextStyle:). Цей метод повертає шрифт, масштабований під поточне системне налаштування:
titleLabel.font = UIFont.preferredFont(forTextStyle: .headline)
bodyLabel.font = UIFont.preferredFont(forTextStyle: .body)
Для кастомних шрифтів використовуйте UIFontMetrics:
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, що й preferredFont. Ви можете задати як fromTextStyle: .body, .headline тощо. Кожен стиль має свій коефіцієнт масштабування. Body масштабується помірно, Large Title — активно. Якщо ваш кастомний шрифт використовується для основного тексту, використовуйте .body.
В SwiftUI підтримка Dynamic Type вбудована за замовчуванням. Усі системні модифікатори (.font(.body), .font(.title)) автоматично масштабуються. Розробнику не потрібно викликати UIFont.preferredFont.
Приклад:
Text("Основний текст")
.font(.body)
.lineLimit(nil)
.minimumScaleFactor(0.5)
Для кастомних масштабів використовуйте DynamicTypeSize:
@Environment(\.dynamicTypeSize) var dynamicTypeSize
var body: some View {
Text("Адаптивний текст")
.font(.body)
.padding(dynamicTypeSize <= .large ? 8 : 16)
}
SwiftUI автоматично оновлює подання при зміні системного розміру тексту — ніяких додаткових підписок на сповіщення.
Модифікатор .font(.body) працює лише для системного шрифту. Для кастомних шрифтів в SwiftUI використовуйте Font.custom з UIFontMetrics під капотом. Починаючи з iOS 16, SwiftUI підтримує DynamicTypeSize в @Environment, що дозволяє адаптувати відступи та компонування.
Auto Layout — обов’язкова умова підтримки Dynamic Type. Фіксовані ширини та висоти (>=, <=) ламаються при accessibility-розмірах. Використовуйте intrinsicContentSize для UILabel — він автоматично обчислює висоту під поточний розмір шрифту.
Правила адаптивної верстки:
Для довгих текстів (наприклад, статті, умови використання) встановіть minimumScaleFactor на UILabel — це зменшує текст в останню чергу, коли Auto Layout не може вмістити контент. Значення 0.5 означає, що шрифт може зменшитися до 50% від прийнятого розміру.
Приклад підбудови відступів під категорію розміру:
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)
Обрізання тексту — UILabel з numberOfLines = 1 та фіксованою шириною обрізає текст на AccessibilityXL. Рішення: numberOfLines = 0 та trailing constraint. Перекриття елементів — кнопки з фіксованою висотою наїзджають одна на одну. Рішення: UIStackView з automatic spacing. Вихід за межі екрану — ScrollView обов’язковий для accessibility-рівнів.
Симулятор Xcode дозволяє змінити розмір тексту: в меню симулятора Settings → Accessibility → Display & Text Size → Larger Text. Перемикайте між усіма 11 рівнями та перевіряйте, що текст не обрізається, кнопки доступні, верстка не ламається.
Для автоматизованого тестування використовуйте налаштування contentSizeCategory в XCUIApplication:
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-рівнях, оскільки розробники тестують лише стандартні розміри.
Xcode Accessibility Inspector показує, які шрифти використовує вибраний елемент — фіксований або масштабований. Якщо елемент не реагує на зміну розміру тексту в симуляторі, значить використовується фіксований шрифт. Замініть його на UIFont.preferredFont або UIFontMetrics.
Часто запитувані питання
Це автоматичне масштабування тексту в додатку під системні налаштування розміру. Користувач змінює розмір в Налаштуваннях — всі підтримувані додатки адаптуються без участі розробника.
Ні. Для кастомних шрифтів використовуйте UIFontMetrics(scaledFont:). Метод масштабує будь-який шрифт відносно заданого текстового стилю, зберігаючи пропорції.
11 рівнів: 5 стандартних (XS, S, M, L, XL) та 6 accessibility-рівнів (від AccessibilityXL до AccessibilityXXXXXL). Розмір шрифту body може варіюватися від 14px до 53px.
В UIKit через UIContentSizeCategory.didChangeNotification. В SwiftUI використовуйте @Environment(\.dynamicTypeSize) — подання оновиться автоматично при зміні системного налаштування.
Користувачі з збільшеним шрифтом побачать обрізаний текст, наїжджаючі елементи та непрацюючі кнопки. Додаток може бути відхилений на App Store Review за порушення accessibility-вимог.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також