@ScaledMetric — property wrapper SwiftUI, který automaticky škáluje číselnou hodnotu podle nastavení Dynamic Type uživatele. Hodnota je zabalena do @ScaledMetric a přepočítána při změně systémové velikosti písma, což zaručuje přístupnost rozhraní pro osoby se zrakovým postižením. Podle Apple Developer Documentation (2026), @ScaledMetric používá stupnici UIFontMetrics pro výpočet relativního měřítka na základě preferred content size category. Více o přístupnosti si přečtěte v materiálu o přístupnosti SwiftUI.
Hlavní body
@ScaledMetric — property wrapper SwiftUI, přidaný v iOS 14, který automaticky škáluje číselnou hodnotu (CGFloat, Int, Double) na aktuální velikost písma Dynamic Type. Na rozdíl od .font(.body) pro písma, @ScaledMetric škáluje libovolné číselné parametry: padding, spacing, cornerRadius, iconSize — vše, co by se mělo proporcionálně zvětšovat při velkém textu.
Hlavním úkolem @ScaledMetric je zajistit accessibility-škálování netextových prvků rozhraní. Když uživatel zvětší písmo v nastavení iOS, tlačítka, ikony a mezery by se měly proporcionálně zvětšit, aby rozhraní zůstalo vyvážené. @ScaledMetric řeší tento úkol automaticky, bez ručního výpočtu násobitelů.
Základní syntaxe @ScaledMetric používá výchozí hodnotu a volitelný parametr relativeTo. Pokud je relativeTo zadáno, škálování je vázáno na konkrétní styl textu (UIFontTextStyle). Pokud ne — používá se stupnice .body.
struct AccessibleButton: View {
@ScaledMetric private var padding: CGFloat = 12
@ScaledMetric(relativeTo: .title) private var iconSize: CGFloat = 24
var body: some View {
Label("Odeslat", systemImage: "checkmark.circle.fill")
.font(.body)
.padding(padding)
.imageScale(.init(rawValue: iconSize / 24) ?? .medium)
}
}
padding bude škálován relativně k .body (výchozí), iconSize — relativně k .title. Při velkém textu se mezery a ikona proporcionálně zvětší. Bez @ScaledMetric by mezery zůstaly 12 pt při jakékoli velikosti písma, což by vedlo k vizuální nevyváženosti.
Mechanismus @ScaledMetric je založen na UIFontMetrics z UIKit. Když SwiftUI vytvoří instanci @ScaledMetric, vypočítá násobitel na základě aktuální preferred content size category (UIContentSizeCategory). Základní hodnota je vynásobena scaledValue z UIFontMetrics pro zadaný styl textu.
Matematicky: ScaledMetricValue = baseValue × UIFontMetrics.scaledValue(for: relativeTo). Pokud relativeTo není zadáno, používá se UIFontMetrics.default vázaný na .body. Při změně Dynamic Type SwiftUI znovu vytvoří tělo View, @ScaledMetric vypočítá novou scaledValue a UI se automaticky aktualizuje prostřednictvím mechanismu PropertyWrappers podobných @State.
Stupnice iOS zahrnuje 11 velikostí: od .extraSmall (5 pt) do .accessibilityExtraExtraExtraLarge (77 pt pro .body). Koeficient škálování pro .body se pohybuje od 0.85 (XS) do 1.71 (XXXL) vzhledem k základní hodnotě. @ScaledMetric používá právě tuto stupnici, takže hodnota 12 pt pro padding se může stát ~20 pt při maximální velikosti accessibility.
| Content Size Category | Koeficient (body) | Příklad @ScaledMetric(12) |
|---|---|---|
| extraSmall | 0.85 | ~10 pt |
| small | 0.93 | ~11 pt |
| medium (default) | 1.00 | 12 pt |
| large | 1.07 | ~13 pt |
| extraLarge | 1.15 | ~14 pt |
| extraExtraLarge | 1.28 | ~15 pt |
| accessibilityExtraLarge | 1.47 | ~18 pt |
| accessibilityXXXL | 1.71 | ~20 pt |
Výběr relativeTo: používejte .body pro hodnoty spojené s hlavním textem (padding, spacing v seznamech), .title pro velké prvky (iconSize, imageSize), .caption pro malé prvky (velikost badge). To zaručuje, že prvky škálují v souladu s okolním textem.
Dynamic Type — funkce iOS, která uživateli umožňuje přizpůsobit velikost systémového písma v Settings → Display & Brightness → Text Size. Změna se aplikuje globálně na všechny aplikace. @ScaledMetric na tuto změnu reaguje automaticky: SwiftUI aktualizuje všechny @ScaledMetric proměnné při změně UIContentSizeCategory.
Důležité: @ScaledMetric škáluje pouze číselné hodnoty, ale přímo nespravuje písma. Pro písma používejte .font() se stylem textu (.body, .title, .headline) — SwiftUI automaticky škáluje písmo. @ScaledMetric doplňuje škálování písma pro padding, spacing a velikosti prvků.
Canvas Preview podporuje Dynamic Type: na panelu nástrojů Canvas je posuvník Text Size (A–A) pro kontrolu UI při různých velikostech písma. Používejte jej s @ScaledMetric, abyste se ujistili, že se mezery a velikosti správně škálují.
struct CardView: View {
@ScaledMetric private var cornerRadius: CGFloat = 16
@ScaledMetric private var spacing: CGFloat = 8
var body: some View {
VStack(spacing: spacing) {
Text("Název karty")
.font(.headline)
Text("Popis s podporou dynamic type")
.font(.body)
}
.padding(spacing * 2)
.background(.regularMaterial)
.cornerRadius(cornerRadius)
}
}
struct CardView_Previews: PreviewProvider {
static var previews: some View {
CardView()
.dynamicTypeSize(.large)
.previewDisplayName("Large")
CardView()
.dynamicTypeSize(.accessibility5)
.previewDisplayName("Accessibility 5")
}
}
cornerRadius se škáluje od 16 pt do ~27 pt při maximální velikosti accessibility. spacing — od 8 do ~14 pt. To zaručuje, že karta zůstává vizuálně vyvážená při jakékoli velikosti písma.
Příklad: ikona s podporou Dynamic Type. Velikost ikon Image(systemName:) se standardně neškáluje pod Dynamic Type. @ScaledMetric řeší tento problém: změnou imageScale nebo velikosti frame na základě aktuálního faktoru měřítka.
struct IconLabel: View {
let title: String
let icon: String
@ScaledMetric private var iconDimension: CGFloat = 28
@ScaledMetric(relativeTo: .body) private var spacing: CGFloat = 6
var body: some View {
HStack(spacing: spacing) {
Image(systemName: icon)
.resizable()
.frame(width: iconDimension, height: iconDimension)
Text(title)
.font(.body)
}
}
}
Příklad: přístupný badge-komponent. Badge s číslem by se měl škálovat proporcionálně k textu. @ScaledMetric pro minimální velikost badge zaručuje, že kulatý badge zůstává viditelný při velkém písmu.
struct BadgeView: View {
let count: Int
@ScaledMetric(relativeTo: .caption) private var badgeSize: CGFloat = 20
@ScaledMetric(relativeTo: .caption) private var fontScale: CGFloat = 1
var body: some View {
ZStack {
Circle()
.fill(.red)
.frame(width: badgeSize, height: badgeSize)
Text("\(count)")
.font(.caption)
.foregroundColor(.white)
.scaleEffect(fontScale)
}
.fixedSize()
}
}
fontScale dodatečně škáluje obsah Circle, aby odpovídal zvětšenému badgeSize. Bez fontScale by se text uvnitř badge nemusel vejít při velkém písmu.
@ScaledMetric a @State — oba property wrappers sledující změny, ale s různými zdroji aktualizace. @State aktualizuje hodnotu při programové změně (přes $stateBinding). @ScaledMetric aktualizuje hodnotu automaticky při změně systémového Dynamic Type, ale neumožňuje přímou změnu hodnoty z kódu.
Klíčový rozdíl: @ScaledMetric — read-only pro vývojáře a write-only pro systém. Nemůžete změnit scaledValue přes setter — je vypočítán SwiftUI na základě základní hodnoty a aktuálního Dynamic Type. @State je naproti tomu zcela spravován vývojářem. Pokud potřebujete hodnotu, která se jak škáluje pod Dynamic Type, tak mění programově — kombinujte @ScaledMetric s @State nebo použijte vypočítanou vlastnost.
| Vlastnost | @ScaledMetric | @State |
|---|---|---|
| Zdroj aktualizace | Dynamic Type (systém) | Programově (vývojář) |
| Typ hodnoty | CGFloat, Int, Double | Libovolný |
| Změna z kódu | Nelze | Lze přes binding |
| Překreslení View | Při změně Dynamic Type | Při změně hodnoty |
| Verze iOS | iOS 14+ | iOS 13+ |
Kombinovaný vzor: pokud potřebujete měnit padding programově (např. animace stisku) a současně škálovat pod Dynamic Type, vytvořte @ScaledMetric pro základní škálovanou hodnotu a @State pro násobitel animace. Konečná hodnota = scaledValue × animationMultiplier.
Chyba 1: použití @ScaledMetric pro písma. @ScaledMetric škáluje čísla, ne písma. Pro písma používejte .font(.body) — SwiftUI automaticky aplikuje Dynamic Type. Nikdy nepoužívejte @ScaledMetric s font(.system(size: scaledSize)) — to ničí systémovou accessibility.
Chyba 2: chybějící relativeTo pro různé prvky. Pokud máte padding (spojený s .body) a iconSize (spojený s .title), uveďte správný relativeTo pro každý. Bez relativeTo se oba budou škálovat podle .body, což povede k neproporcionálnímu zvětšení ikony vzhledem k jejímu textovému kontextu.
Chyba 3: @ScaledMetric v ViewModel/@ObservableObject. @ScaledMetric je SwiftUI property wrapper fungující pouze uvnitř View. Nelze jej používat ve ViewModel nebo službách. Pro škálování ve ViewModel předejte škálovanou hodnotu z View jako parametr nebo použijte @Environment(\.sizeCategory) ve View.
@Environment(\.sizeCategory) — alternativní způsob získání aktuálního Dynamic Type ve View. Použijte jej, když potřebujete větší kontrolu: vypočítat vlastní násobitel, předat sizeCategory do ViewModel nebo kombinovat s @ScaledMetric pro flexibilní škálování.
struct CustomScaledView: View {
@Environment(\.sizeCategory) private var sizeCategory
@ScaledMetric private var basePadding: CGFloat = 12
private var extraPadding: CGFloat {
if sizeCategory >= .accessibilityLarge {
return basePadding * 0.5
}
return 0
}
var body: some View {
Text("Vlastní škálovaný obsah")
.font(.body)
.padding(basePadding + extraPadding)
}
}
Dodatečná mezera extraPadding se přidává pouze při accessibility velikostech, poskytuje více prostoru pro velký text bez změny základní @ScaledMetric logiky.
Často kladené otázky
@ScaledMetric — oficiální property wrapper SwiftUI pro škálování čísel. @ScaledFont neexistuje jako standardní API — je to vlastní wrapper implementovaný komunitou. Pro písma vždy používejte vestavěný .font() se styly textu (.body, .title) a @ScaledMetric — pro padding, spacing a velikosti.
@ScaledMetric je dostupný na iOS 14+, watchOS 7+, tvOS 14+ a macOS 11+. Na watchOS je Dynamic Type omezen na menší rozsah — velikosti od .extraSmall do .extraLarge bez accessibility velikostí. Na tvOS Dynamic Type chybí — @ScaledMetric vždy vrací základní hodnotu.
Ano, pro testování @ScaledMetric vytvořte View s @ScaledMetric a předejte environment hodnotu .sizeCategory přes .environment(\.sizeCategory, .extraExtraLarge). Poté získejte velikost prvku přes GeometryReader nebo SwiftUI Inspector. Alternativně ověřte logiku škálování přes UIFontMetrics v samostatném modulu.
.dynamicTypeSize — modifikátor View omezující maximální Dynamic Type pro hierarchii (např. .dynamicTypeSize(...large)). @ScaledMetric bere toto omezení v úvahu: pokud je .dynamicTypeSize nastaven, škálovaná hodnota nepřekročí odpovídající velikost. Kombinujte obě API pro přesnou kontrolu.
Ujistěte se, že View používá @ScaledMetric uvnitř sebe (ne ve ViewModel). Zkontrolujte, že View odebírá Dynamic Type: @ScaledMetric automaticky spouští obnovení body, ale pokud View používá .equatable() nebo .id(), mechanismus se může rozbít. Použijte @Environment(\.sizeCategory) jako fallback.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také