Основни точки
Dynamic Type — технология на Apple, която позволява на шрифтовете на приложението автоматично да се мащабират при промяна на системния размер на текста. Потребителят задава предпочитания размер в Settings → Accessibility → Display & Text Size и всички приложения, поддържащи Dynamic Type, адаптират размера на шрифта без рестартиране.
Системата предлага 11 степени на мащабиране: от XS (14pt базов) до XXXL (23pt базов), както и 5 допълнителни размера за достъпност (AX1–AX5) за потребители със сериозни зрителни увреждания. При промяна на размера iOS генерира известие UIContentSizeCategory.didChangeNotification и приложението трябва да презареди шрифтовете чрез UIFontMetrics. Според Apple Human Interface Guidelines, Dynamic Type не променя междуредието и пропорциите на буквите — само размера на шрифта, което запазва четимостта при всички размери.
Dynamic Type е задължителен за приложения, стремящи се към статус на достъпност (accessibility). На WWDC 2024 Apple обяви, че приложения без поддръжка на динамичен тип рискуват отхвърляне при ревю, ако основната им функционалност е свързана с четене на текст. В проектите на IT Sectr внедряваме Dynamic Type от първата версия — това спестява до 20% време за последваща адаптация към достъпност.
Apple предоставя 11 предварително дефинирани текстови стила, всеки от които се показва в UIFontDescriptor.UIAttributes за системния шрифт SF Pro. Разработчикът не задава размер в пиксели — избира семантичен стил, а iOS прилага подходящия размер на шрифта в зависимост от настройките на потребителя.
| Стил | Размер (по подразбиране) | Размер (AX5) | Предназначение |
|---|---|---|---|
| .largeTitle | 34pt | 62pt | Заглавия на екрани от първо ниво |
| .title1 | 28pt | 50pt | Второстепенни заглавия |
| .title2 | 22pt | 40pt | Заглавия на секции |
| .title3 | 20pt | 36pt | Подзаглавия |
| .headline | 17pt semibold | 31pt | Подчертан текст в списъци |
| .body | 17pt | 31pt | Основен текст |
| .callout | 16pt | 29pt | Извиквания и допълнителен текст |
| .subheadline | 15pt | 27pt | Описания на елементи |
| .footnote | 13pt | 23pt | Бележки под линия и малък текст |
| .caption1 | 12pt | 21pt | Описания на изображения |
| .caption2 | 11pt | 19pt | Етикети и малки описания |
Всеки стил използва системния шрифт SF Pro с фиксирано начертание (Regular за body, Bold за headline). Персонализираните шрифтове се свързват чрез UIFontMetrics — единствения правилен начин за адаптиране на шрифт на трета страна към динамичен тип.
UIFontMetrics — клас, въведен в iOS 11, който мащабира произволен UIFont пропорционално на избрания текстов стил. Преди iOS 11 разработчиците използваха директно извикване на preferredFontForTextStyle, което връща само системния шрифт SF Pro. UIFontMetrics решава този проблем: приема всеки шрифт и връща неговата мащабирана версия, запазвайки начертанието и пропорциите.
Методът scaledFont(for:) изчислява коефициента на мащабиране въз основа на текущия размер на текста на потребителя: ако е избран размер "Голям" (Large), коефициентът е 1.0; ако "Много голям" (AX1) — приблизително 1.3; за "Максимален" (AX5) — до 1.8. Коефициентът се прилага към базовия размер на персонализирания шрифт, а не към системния — това гарантира, че дизайнерското намерение се запазва при всички размери.
UIFontMetrics се препоръчва за всички персонализирани шрифтове в приложението. На WWDC 2024 Apple показа инструмента Accessibility Inspector, който проверява поддръжката на Dynamic Type и подчертава елементи, използващи фиксиран размер вместо UIFontMetrics. В IT Sectr внедрихме UIFontMetrics във всички проекти от 2018 г. — това намали броя на грешките с припокриващ се текст с 35%.
Най-простият начин да получите динамичен шрифт за етикет. Кодът показва как да настроите UILabel със стил .body, който автоматично реагира на промяна на системния размер на текста.
import UIKit
class GreetingViewController: UIViewController {
private let label: UILabel = {
let l = UILabel()
l.text = "Hello, Dynamic Type!"
l.font = UIFont.preferredFont(forTextStyle: .body)
l.adjustsFontForContentSizeCategory = true
l.translatesAutoresizingMaskIntoConstraints = false
return l
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(label)
NSLayoutConstraint.activate([
label.centerXAnchor.constraint(equalTo: view.centerXAnchor),
label.centerYAnchor.constraint(equalTo: view.centerYAnchor),
])
}
}
Флагът adjustsFontForContentSizeCategory = true автоматично актуализира шрифта при промяна на системния размер на текста — без този флаг Dynamic Type няма да работи за съществуващия етикет. При промяна на размера iOS извиква layoutIfNeeded и етикетът актуализира шрифта без ръчна намеса.
При използване на персонализиран шрифт (напр. Montserrat), извикването на preferredFontForTextStyle ще върне SF Pro, а не вашия шрифт. UIFontMetrics мащабира точно персонализирания шрифт, запазвайки неговия дизайн.
import UIKit
extension UIFont {
static func customDynamicFont(name: String,
size: CGFloat,
style: UIFont.TextStyle) -> UIFont {
guard let baseFont = UIFont(name: name, size: size) else {
return .preferredFont(forTextStyle: style)
}
return UIFontMetrics(forTextStyle: style).scaledFont(for: baseFont)
}
}
let titleFont = UIFont.customDynamicFont(
name: "Montserrat-Bold",
size: 20,
style: .title3
)
Методът scaledFont(for:) приема базов UIFont и връща мащабирана версия, пропорционална на стила .title3. При размер за достъпност AX5 шрифтът Montserrat-Bold 20pt се превръща в ~36pt — точно колкото би се увеличил системният шрифт на този стил. В IT Sectr използваме този модел във всички проекти с персонализирани шрифтове от 2018 г., което напълно елиминира оплакванията за малък текст от потребители с ограничено зрение.
Ако актуализирате шрифта ръчно (без adjustsFontForContentSizeCategory), абонирайте се за известието UIContentSizeCategory.didChangeNotification. Това е необходимо за персонализирани UI елементи, които не наследяват автоматично актуализиране на шрифта.
import UIKit
class DynamicLabel: UILabel {
private var customFont: UIFont?
private var textStyle: UIFont.TextStyle = .body
func configure(font: UIFont, style: UIFont.TextStyle) {
customFont = font
textStyle = style
NotificationCenter.default.addObserver(
self,
selector: #selector(updateFont),
name: UIContentSizeCategory.didChangeNotification,
object: nil
)
updateFont()
}
@objc private func updateFont() {
guard let base = customFont else { return }
self.font = UIFontMetrics(forTextStyle: textStyle).scaledFont(for: base)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
}
Абонирането за didChangeNotification гарантира, че персонализираният елемент актуализира шрифта при всяка промяна на системните настройки без рестартиране на приложението. От iOS 15 нататък известието пристига дори при промяна на размера на текста чрез Control Center в приложения, поддържащи Live Text Size.
Често задавани въпроси
Dynamic Type е достъпен на iOS 7 и по-нови версии. UIFontMetrics се появи в iOS 11 — преди него се използваше директно методът preferredFontForTextStyle. На iOS 15+ динамичният тип поддържа допълнителни размери за достъпност (AX1–AX5) за големи шрифтове. Приложенията, поддържащи iOS 10 и по-стари, използват preferredFontForTextStyle без UIFontMetrics, което ограничава поддръжката само до системния шрифт SF Pro.
В Xcode 15+ отворете симулатора и изберете Settings → Accessibility → Display & Text Size → Larger Text. Плъзнете плъзгача за промяна на размера. В самия Xcode има Debug → Accessibility Inspector, който подчертава елементи с фиксиран шрифт. За автоматизация използвайте тест, който променя UISlider настройките чрез XCUITest — това гарантира, че всички екрани се преустройват правилно.
Да, Auto Layout с динамичен тип изисква използване на intrinsicContentSize в UILabel и UIButton. При размери за достъпност текстът може да излезе извън границите на superview — добавете UIScrollView или увеличете leading/trailing ограниченията. Използвайте UIFontMetrics.scaledValue за разстояния (padding), така че междубуквените интервали също да се мащабират пропорционално на шрифта.
Обобщение
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също