@ScaledMetric — mi ez, property wrapper SwiftUI és Dynamic Type

Szerző: IT Sectr Megjelenés: 2026-06-27 Olvasási idő: 10 perc

@ScaledMetric — SwiftUI property wrapper, amely automatikusan skálázza a numerikus értéket a felhasználó Dynamic Type beállításainak megfelelően. Az érték @ScaledMetric-be van csomagolva és újraszámolásra kerül a rendszer betűméretének változásakor, garantálva a felület hozzáférhetőségét látássérült emberek számára. Az Apple Developer Documentation (2026) szerint az @ScaledMetric az UIFontMetrics skálát használja a relatív skála kiszámításához a preferred content size category alapján. További információ az akadálymentesítésről: anyag a SwiftUI akadálymentesítésről.

Főbb pontok

  • @ScaledMetric — SwiftUI property wrapper értékek skálázásához Dynamic Type alatt.
  • Dynamic Type — iOS rendszerbeállítás, amely a betűméretet UIFontTextStyle-ből módosítja.
  • Skálázás — @ScaledMetric elfogadja az alapértéket és a relativeTo szorzót.
  • Automatikus frissítés — Dynamic Type változásakor az @ScaledMetric újraszámolódik és a UI frissül.
  • Accessibility — az @ScaledMetric használata javítja a felület akadálymentesítését extra kód nélkül.

Mi az @ScaledMetric?

@ScaledMetric — iOS 14-ben hozzáadott SwiftUI property wrapper, amely automatikusan skálázza a numerikus értéket (CGFloat, Int, Double) az aktuális Dynamic Type betűméretre. Ellentétben a .font(.body) betűtípusokhoz való használatával, az @ScaledMetric bármilyen numerikus paramétert skáláz: padding, spacing, cornerRadius, iconSize — mindent, aminek arányosan kell nőnie nagy szövegnél.

Az @ScaledMetric fő feladata a nem szöveges felületelemek akadálymentesítési skálázásának biztosítása. Amikor a felhasználó megnöveli a betűméretet az iOS beállításaiban, a gomboknak, ikonoknak és távolságoknak arányosan kell nőniük, hogy a felület egyensúlyban maradjon. Az @ScaledMetric automatikusan oldja meg ezt a feladatot, a szorzók kézi kiszámítása nélkül.

Az @ScaledMetric szintaxisa

Alap szintaxis Az @ScaledMetric alapértelmezett értéket és opcionális relativeTo paramétert használ. Ha a relativeTo meg van adva, a skálázás egy adott szövegstílushoz (UIFontTextStyle) van kötve. Ha nincs — a .body skála használatos.

swift
struct AccessibleButton: View {
    @ScaledMetric private var padding: CGFloat = 12
    @ScaledMetric(relativeTo: .title) private var iconSize: CGFloat = 24
    
    var body: some View {
        Label("Küldés", systemImage: "checkmark.circle.fill")
            .font(.body)
            .padding(padding)
            .imageScale(.init(rawValue: iconSize / 24) ?? .medium)
    }
}

padding a .body-hoz (alapértelmezett), iconSize a .title-hez képest skálázódik. Nagy szövegnél a távolságok és az ikon arányosan nőnek. @ScaledMetric nélkül a távolságok 12 pt maradnának bármilyen betűméretnél, ami vizuális egyensúlyhiányhoz vezetne.

Hogyan működik az @ScaledMetric

Az @ScaledMetric mechanizmusa a UIKit-ből származó UIFontMetrics-en alapul. Amikor a SwiftUI létrehoz egy @ScaledMetric példányt, kiszámít egy szorzót az aktuális preferred content size category (UIContentSizeCategory) alapján. Az alapérték megszorzásra kerül a UIFontMetrics scaledValue-jával a megadott szövegstílushoz.

Matematikailag: ScaledMetricValue = baseValue × UIFontMetrics.scaledValue(for: relativeTo). Ha a relativeTo nincs megadva, a .body-hoz kötött UIFontMetrics.default használatos. Dynamic Type változásakor a SwiftUI újra létrehozza a View törzsét, az @ScaledMetric kiszámítja az új scaledValue-ot, és a UI automatikusan frissül a @State-szerű PropertyWrappers mechanizmusán keresztül.

A Dynamic Type skálázási skálája

Az iOS skála 11 méretet tartalmaz: .extraSmall (5 pt) és .accessibilityExtraExtraExtraLarge (77 pt .body esetén) között. A .body skálázási együtthatója 0.85 (XS) és 1.71 (XXXL) között változik az alapértékhez képest. Az @ScaledMetric pontosan ezt a skálát használja, így a 12 pt padding érték ~20 pt lehet a maximális akadálymentesítési méretnél.

Content Size CategoryEgyüttható (body)Példa @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

relativeTo kiválasztása: használja a .body-t a fő szöveghez kapcsolódó értékekhez (padding, spacing listákban), .title-t nagy elemekhez (iconSize, imageSize), .caption-t kis elemekhez (badge méret). Ez biztosítja, hogy az elemek a környező szöveggel összhangban skálázódjanak.

@ScaledMetric és Dynamic Type

Dynamic Type — iOS funkció, amely lehetővé teszi a felhasználónak a rendszer betűméretének beállítását a Settings → Display & Brightness → Text Size menüpontban. A változás globálisan érvényesül az összes alkalmazásra. Az @ScaledMetric automatikusan reagál erre a változásra: a SwiftUI frissíti az összes @ScaledMetric változót a UIContentSizeCategory változásakor.

Fontos: Az @ScaledMetric csak numerikus értékeket skáláz, de nem kezeli közvetlenül a betűtípusokat. Betűtípusokhoz használja a .font()-ot szövegstílussal (.body, .title, .headline) — a SwiftUI automatikusan skálázza a betűtípust. Az @ScaledMetric kiegészíti a betűtípus skálázást padding, spacing és elem méretek esetében.

Akadálymentesítés ellenőrzése a Canvas segítségével

Canvas Preview támogatja a Dynamic Type-ot: a Canvas eszköztárán van egy Text Size (A–A) csúszka a UI ellenőrzéséhez különböző betűméreteknél. Használja az @ScaledMetric-kel, hogy megbizonyosodjon a távolságok és méretek helyes skálázódásáról.

swift
struct CardView: View {
    @ScaledMetric private var cornerRadius: CGFloat = 16
    @ScaledMetric private var spacing: CGFloat = 8
    
    var body: some View {
        VStack(spacing: spacing) {
            Text("Kártya címe")
                .font(.headline)
            Text("Leírás dynamic type támogatással")
                .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 16 pt-ról ~27 pt-ra skálázódik maximális akadálymentesítési méretnél. spacing — 8-ról ~14 pt-ra. Ez biztosítja, hogy a kártya vizuálisan egyensúlyban maradjon bármilyen betűméretnél.

@ScaledMetric példák

Példa: ikon Dynamic Type támogatással. Az Image(systemName:) ikonok mérete alapértelmezés szerint nem skálázódik Dynamic Type alatt. Az @ScaledMetric megoldja ezt a problémát: az imageScale vagy a frame méret módosításával az aktuális skálázási tényező alapján.

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élda: akadálymentes badge-komponens. A számmal ellátott badge-nek arányosan kell skálázódnia a szöveggel. Az @ScaledMetric a badge minimális méretéhez biztosítja, hogy a kerek badge látható maradjon nagy betűméretnél.

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 emellett skálázza a Circle tartalmát a megnövelt badgeSize-hoz igazodva. fontScale nélkül a badge-en belüli szöveg nem férne el nagy betűméretnél.

@ScaledMetric vs @State — eltérések

@ScaledMetric és @State — mindkettő property wrapper, amelyek változásokat követnek, de eltérő frissítési forrásokkal. A @State programozott változásnál frissíti az értéket ($stateBinding-en keresztül). Az @ScaledMetric automatikusan frissíti az értéket a rendszer Dynamic Type változásakor, de nem engedi az érték közvetlen módosítását kódból.

A kulcsfontosságú különbség: @ScaledMetric — read-only a fejlesztő számára és write-only a rendszer számára. Nem módosíthatja a scaledValue-t setteren keresztül — a SwiftUI számítja ki az alapérték és az aktuális Dynamic Type alapján. A @State ezzel szemben teljesen a fejlesztő által kezelt. Ha olyan értékre van szüksége, amely egyszerre skálázódik Dynamic Type alatt és programozottan is változtatható — kombinálja az @ScaledMetric-et a @State-tel, vagy használjon számított tulajdonságot.

Jellemző@ScaledMetric@State
Frissítés forrásaDynamic Type (rendszer)Programozott (fejlesztő)
Érték típusaCGFloat, Int, DoubleBármilyen
Módosítás kódbólNem lehetBinding-en keresztül lehet
View újrarajzolásaDynamic Type változásakorÉrték változásakor
iOS verzióiOS 14+iOS 13+

Kombinált minta: ha programozottan kell módosítania a padding-et (pl. kattintás animáció) és egyidejűleg skáláznia Dynamic Type alatt, hozzon létre egy @ScaledMetric-et az alap skálázott értékhez és egy @State-t az animációs szorzóhoz. A végső érték = scaledValue × animationMultiplier.

Gyakori hibák az @ScaledMetric-kel

Hiba 1: @ScaledMetric használata betűtípusokhoz. Az @ScaledMetric számokat skáláz, nem betűtípusokat. Betűtípusokhoz használja a .font(.body)-t — a SwiftUI automatikusan alkalmazza a Dynamic Type-ot. Soha ne használja az @ScaledMetric-et font(.system(size: scaledSize))-val — ez tönkreteszi a rendszer akadálymentesítését.

Hiba 2: relativeTo hiánya heterogén elemeknél. Ha padding (.body-hoz kapcsolódó) és iconSize (.title-hez kapcsolódó) is van, adja meg a helyes relativeTo-t mindegyikhez. relativeTo nélkül mindkettő .body szerint skálázódik, ami az ikon aránytalan növekedéséhez vezet a szöveges kontextusához képest.

Hiba 3: @ScaledMetric ViewModel/@ObservableObject osztályban. Az @ScaledMetric egy SwiftUI property wrapper, amely csak View-n belül működik. Nem használható ViewModel-ben vagy szolgáltatásokban. A ViewModel-ben történő skálázáshoz adja át a skálázott értéket a View-ból paraméterként, vagy használja az @Environment(\.sizeCategory)-t a View-ban.

Aktuális sizeCategory lekérése kódból

@Environment(\.sizeCategory) — alternatív módszer az aktuális Dynamic Type lekérésére a View-ban. Használja, amikor több vezérlésre van szüksége: egyéni szorzó kiszámítása, sizeCategory átadása ViewModel-nek vagy kombinálás @ScaledMetric-kel a rugalmas skálázáshoz.

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("Egyéni skálázott tartalom")
            .font(.body)
            .padding(basePadding + extraPadding)
    }
}

Extra távolság Az extraPadding csak akadálymentesítési méreteknél adódik hozzá, több helyet biztosítva a nagy szöveg számára az alap @ScaledMetric logika megváltoztatása nélkül.

Gyakran ismételt kérdések

Miben különbözik az @ScaledMetric a @ScaledFont-tól?

@ScaledMetric — hivatalos SwiftUI property wrapper számok skálázásához. @ScaledFont nem létezik szabványos API-ként — ez egy közösség által megvalósított egyéni wrapper. Betűtípusokhoz mindig a beépített .font()-ot használja szövegstílusokkal (.body, .title), az @ScaledMetric-et pedig padding, spacing és méretek esetén.

Működik az @ScaledMetric watchOS és tvOS rendszeren?

@ScaledMetric iOS 14+, watchOS 7+, tvOS 14+ és macOS 11+ rendszeren érhető el. watchOS-en a Dynamic Type kisebb tartományra korlátozott — .extraSmall és .extraLarge közötti méretek akadálymentesítési méretek nélkül. tvOS-en a Dynamic Type hiányzik — az @ScaledMetric mindig az alapértéket adja vissza.

Tesztelhető az @ScaledMetric unit-tesztekkel?

Igen, az @ScaledMetric teszteléséhez hozzon létre egy View-t @ScaledMetric-kel, és adja át a .sizeCategory environment értéket .environment(\.sizeCategory, .extraExtraLarge) segítségével. Ezután szerezze be az elem méretét GeometryReader vagy SwiftUI Inspector segítségével. Alternatívaként ellenőrizze a skálázási logikát UIFontMetrics segítségével egy külön modulban.

Hogyan hat kölcsön az @ScaledMetric a .dynamicTypeSize-szal?

.dynamicTypeSize — View módosító, amely korlátozza a maximális Dynamic Type-ot egy hierarchiában (pl. .dynamicTypeSize(...large)). Az @ScaledMetric figyelembe veszi ezt a korlátozást: ha a .dynamicTypeSize be van állítva, a skálázott érték nem haladja meg a megfelelő méretet. Kombinálja mindkét API-t a pontos vezérléshez.

Mi a teendő, ha az @ScaledMetric nem frissíti a UI-t?

Győződjön meg arról, hogy a View az @ScaledMetric-et saját magában használja (nem ViewModel-ben). Ellenőrizze, hogy a View feliratkozott-e a Dynamic Type-ra: az @ScaledMetric automatikusan kiváltja a body frissítést, de ha a View .equatable()-t vagy .id()-t használ, a mechanizmus megszakadhat. Használja az @Environment(\.sizeCategory)-t fallback-ként.

Összefoglalás

  • @ScaledMetric — SwiftUI property wrapper számok automatikus skálázásához Dynamic Type alatt.
  • Kötés — a relativeTo a skálát egy adott szövegstílushoz (body, title, caption) köti.
  • Accessibility — az @ScaledMetric javítja a felület akadálymentesítését kézi kódolás nélkül.
  • Csak számok — a wrapper CGFloat, Int, Double értékeket skáláz, de nem betűtípusokat.
  • Tartomány — 0.85 (XS) és 1.71 (XXXL) között az alapértékhez képest.
  • Read-only — az @ScaledMetric kódból nem módosítható, csak a rendszeren keresztül.

Kulcsrakész mobilalkalmazást fejlesztünk

Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.

Projekt megbeszélése

Olvassa el is