Najważniejsze
Dynamic Type — technologia Apple umożliwiająca automatyczne skalowanie czcionek aplikacji przy zmianie systemowego rozmiaru tekstu. Użytkownik ustawia preferowany rozmiar w Ustawienia → Dostępność → Wyświetlacz i rozmiar tekstu, a wszystkie aplikacje obsługujące Dynamic Type dostosowują stopień pisma bez restartu.
System oferuje 11 stopni skalowania: od XS (14pt bazowy) do XXXL (23pt bazowy), a także 5 dodatkowych rozmiarów dostępności (AX1–AX5) dla użytkowników z poważnymi zaburzeniami wzroku. Przy zmianie rozmiaru iOS generuje powiadomienie UIContentSizeCategory.didChangeNotification, a aplikacja musi ponownie wczytać czcionki przez UIFontMetrics. Według Apple Human Interface Guidelines, Dynamic Type nie zmienia odstępów międzywierszowych ani proporcji liter — tylko stopień pisma, co zachowuje czytelność we wszystkich rozmiarach.
Dynamic Type jest wymagany dla aplikacji aspirujących do statusu dostępnych (accessibility). Na WWDC 2024 Apple ogłosiło, że aplikacje bez obsługi dynamicznego typu ryzykują odrzucenie podczas recenzji, jeśli ich główna funkcjonalność związana jest z czytaniem tekstu. W projektach IT Sectr wdrażamy Dynamic Type od pierwszej wersji — oszczędza to do 20% czasu na późniejszą adaptację pod kątem dostępności.
Apple udostępnia 11 predefiniowanych stylów tekstowych, z których każdy jest wyświetlany w UIFontDescriptor.UIAttributes dla systemowej czcionki SF Pro. Deweloper nie określa rozmiaru w pikselach — wybiera styl semantyczny, a iOS stosuje odpowiedni stopień pisma w zależności od ustawień użytkownika.
| Styl | Stopień (domyślny) | Stopień (AX5) | Przeznaczenie |
|---|---|---|---|
| .largeTitle | 34pt | 62pt | Nagłówki ekranów pierwszego poziomu |
| .title1 | 28pt | 50pt | Nagłówki drugorzędne |
| .title2 | 22pt | 40pt | Nagłówki sekcji |
| .title3 | 20pt | 36pt | Podtytuły |
| .headline | 17pt semibold | 31pt | Wyróżniony tekst na listach |
| .body | 17pt | 31pt | Tekst podstawowy |
| .callout | 16pt | 29pt | Odwołania i dodatkowy tekst |
| .subheadline | 15pt | 27pt | Podpisy elementów |
| .footnote | 13pt | 23pt | Przypisy i drobny tekst |
| .caption1 | 12pt | 21pt | Podpisy do obrazów |
| .caption2 | 11pt | 19pt | Etykiety i drobne podpisy |
Każdy styl używa systemowej czcionki SF Pro o stałym kroju (Regular dla body, Bold dla headline). Niestandardowe czcionki są podłączane przez UIFontMetrics — jedyny poprawny sposób adaptacji czcionki zewnętrznej do dynamicznego typu.
UIFontMetrics — klasa wprowadzona w iOS 11, która skaluje dowolny UIFont proporcjonalnie do wybranego stylu tekstowego. Przed iOS 11 deweloperzy używali bezpośredniego wywołania preferredFontForTextStyle, które zwraca tylko systemową czcionkę SF Pro. UIFontMetrics rozwiązuje ten problem: przyjmuje dowolną czcionkę i zwraca jej przeskalowaną wersję, zachowując krój i proporcje.
Metoda scaledFont(for:) oblicza współczynnik skalowania na podstawie bieżącego rozmiaru tekstu użytkownika: jeśli wybrany jest rozmiar „Duży" (Large), współczynnik wynosi 1.0; jeśli „Bardzo duży" (AX1) — około 1.3; dla „Maksymalny" (AX5) — do 1.8. Współczynnik jest stosowany do bazowego rozmiaru niestandardowej czcionki, a nie do systemowej — gwarantuje to zachowanie zamysłu projektanta we wszystkich rozmiarach.
UIFontMetrics jest zalecany do używania ze wszystkimi niestandardowymi czcionkami w aplikacji. Na WWDC 2024 Apple pokazało narzędzie Accessibility Inspector, które sprawdza obsługę Dynamic Type i podświetla elementy używające stałego stopnia zamiast UIFontMetrics. W IT Sectr wdrożyliśmy UIFontMetrics we wszystkich projektach od 2018 roku — zmniejszyło to liczbę błędów z nakładającym się tekstem o 35%.
Najprostszy sposób uzyskania dynamicznej czcionki dla etykiety. Kod pokazuje, jak ustawić UILabel ze stylem .body, który automatycznie reaguje na zmianę systemowego rozmiaru tekstu.
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),
])
}
}
Flaga adjustsFontForContentSizeCategory = true automatycznie aktualizuje czcionkę przy zmianie systemowego rozmiaru tekstu — bez tej flagi Dynamic Type nie zadziała dla istniejącej etykiety. Przy zmianie rozmiaru iOS wywołuje layoutIfNeeded, a label aktualizuje czcionkę bez ręcznej ingerencji.
W przypadku używania niestandardowej czcionki (np. Montserrat) wywołanie preferredFontForTextStyle zwróci SF Pro, a nie twoją czcionkę. UIFontMetrics skaluje właśnie niestandardową czcionkę, zachowując jej design.
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
)
Metoda scaledFont(for:) przyjmuje bazowy UIFont i zwraca przeskalowaną wersję proporcjonalną do stylu .title3. Przy rozmiarze dostępności AX5 czcionka Montserrat-Bold 20pt zmieni się na ~36pt — dokładnie o tyle, o ile zwiększyłaby się systemowa czcionka tego stylu. W IT Sectr używamy tego wzorca we wszystkich projektach z niestandardowymi czcionkami od 2018 roku, co całkowicie wyeliminowało skargi na mały tekst od użytkowników z ograniczeniami wzroku.
Jeśli aktualizujesz czcionkę ręcznie (bez adjustsFontForContentSizeCategory), zasubskrybuj powiadomienie UIContentSizeCategory.didChangeNotification. Jest to konieczne dla niestandardowych elementów UI, które nie dziedziczą automatycznej aktualizacji czcionki.
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)
}
}
Subskrypcja didChangeNotification gwarantuje, że niestandardowy element zaktualizuje czcionkę przy każdej zmianie ustawień systemowych bez restartu aplikacji. Od iOS 15 powiadomienie przychodzi nawet przy zmianie rozmiaru tekstu przez Control Center w aplikacjach obsługujących Live Text Size.
Często zadawane pytania
Dynamic Type jest dostępny na iOS 7 i nowszych. UIFontMetrics pojawił się w iOS 11 — przed nim używano metody preferredFontForTextStyle bezpośrednio. Na iOS 15+ dynamiczny typ obsługuje dodatkowe rozmiary dostępności (AX1–AX5) dla dużych czcionek. Aplikacje obsługujące iOS 10 i starsze używają preferredFontForTextStyle bez UIFontMetrics, co ogranicza obsługę tylko do systemowej czcionki SF Pro.
W Xcode 15+ otwórz symulator i wybierz Settings → Accessibility → Display & Text Size → Larger Text. Przeciągnij suwak, aby zmienić rozmiar. W samym Xcode jest Debug → Accessibility Inspector, który podświetla elementy ze stałą czcionką. Do automatyzacji użyj testu zmieniającego UISlider ustawień przez XCUITest — gwarantuje to, że wszystkie ekrany poprawnie się przebudowują.
Tak, Auto Layout z dynamicznym typem wymaga używania intrinsicContentSize w UILabel i UIButton. Przy rozmiarach dostępności tekst może wychodzić poza granice superview — dodaj UIScrollView lub zwiększ leading/trailing constraints. Używaj UIFontMetrics.scaledValue dla odstępów (padding), aby odstępy między znakami również skalowały się proporcjonalnie do czcionki.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również