@ScaledMetric — co to je, property wrapper SwiftUI a Dynamic Type

Autor: IT Sectr Publikováno: 2026-06-27 Doba čtení: 10 min

@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 pro škálování hodnot pod Dynamic Type.
  • Dynamic Type — systémové nastavení iOS měnící velikost písma z UIFontTextStyle.
  • Škálování — @ScaledMetric přijímá základní hodnotu a násobitel relativeTo.
  • Automatická aktualizace — při změně Dynamic Type se @ScaledMetric přepočítá a UI se aktualizuje.
  • Accessibility — použití @ScaledMetric zlepšuje přístupnost rozhraní bez dodatečného kódu.

Co je @ScaledMetric?

@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ů.

Syntaxe @ScaledMetric

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.

swift
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.

Jak funguje @ScaledMetric

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 škálování Dynamic Type

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 CategoryKoeficient (body)Příklad @ScaledMetric(12)
extraSmall0.85~10 pt
small0.93~11 pt
medium (default)1.0012 pt
large1.07~13 pt
extraLarge1.15~14 pt
extraExtraLarge1.28~15 pt
accessibilityExtraLarge1.47~18 pt
accessibilityXXXL1.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.

@ScaledMetric a Dynamic Type

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ů.

Kontrola accessibility přes Canvas

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í.

swift
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říklady @ScaledMetric

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.

swift
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.

swift
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 vs @State — rozdíly

@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 aktualizaceDynamic Type (systém)Programově (vývojář)
Typ hodnotyCGFloat, Int, DoubleLibovolný
Změna z kóduNelzeLze přes binding
Překreslení ViewPři změně Dynamic TypePři změně hodnoty
Verze iOSiOS 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.

Typické chyby s @ScaledMetric

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.

Získání aktuální sizeCategory v kódu

@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í.

swift
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

Čím se @ScaledMetric liší od @ScaledFont?

@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.

Funguje @ScaledMetric na watchOS a tvOS?

@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.

Lze @ScaledMetric testovat v unit-testech?

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.

Jak @ScaledMetric interaguje s .dynamicTypeSize?

.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.

Co dělat, když @ScaledMetric neaktualizuje UI?

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í

  • @ScaledMetric — property wrapper SwiftUI pro automatické škálování čísel pod Dynamic Type.
  • Vázání — relativeTo váže měřítko ke konkrétnímu stylu textu (body, title, caption).
  • Accessibility — @ScaledMetric zlepšuje přístupnost rozhraní bez ručního kódování.
  • Pouze čísla — wrapper škáluje CGFloat, Int, Double, ale ne písma.
  • Rozsah — od 0.85 (XS) do 1.71 (XXXL) vzhledem k základní hodnotě.
  • Read-only — @ScaledMetric nelze změnit z kódu, pouze přes systém.

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í.

Prodiskutovat projekt

Přečtěte si také