@ScaledMetric — vad är det, property wrapper SwiftUI och Dynamic Type

Författare: IT Sectr Publicerad: 2026-06-27 Lästid: 10 min

@ScaledMetric — property wrapper SwiftUI som automatiskt skalar ett numeriskt värde enligt användarens Dynamic Type-inställningar. Värdet slås in i @ScaledMetric och räknas om när systemets teckenstorlek ändras, vilket garanterar gränssnittets tillgänglighet för personer med synnedsättning. Enligt Apple Developer Documentation (2026) använder @ScaledMetric UIFontMetrics-skalan för att beräkna relativ skala baserat på preferred content size category. Läs mer om tillgänglighet i materialet om tillgänglighet i SwiftUI.

Huvudpunkter

  • @ScaledMetric — property wrapper SwiftUI för skalning av värden under Dynamic Type.
  • Dynamic Type — systeminställning i iOS som ändrar teckenstorlek från UIFontTextStyle.
  • Skalning — @ScaledMetric accepterar basvärde och relativeTo-multiplikator.
  • Automatisk uppdatering — när Dynamic Type ändras räknas @ScaledMetric om och gränssnittet uppdateras.
  • Accessibility — användning av @ScaledMetric förbättrar gränssnittets tillgänglighet utan extra kod.

Vad är @ScaledMetric?

@ScaledMetric — property wrapper SwiftUI, tillagt i iOS 14, som automatiskt skalar ett numeriskt värde (CGFloat, Int, Double) till aktuell Dynamic Type-teckenstorlek. Till skillnad från .font(.body) för teckensnitt, skalar @ScaledMetric alla numeriska parametrar: padding, spacing, cornerRadius, iconSize — allt som bör öka proportionellt vid stor text.

Huvuduppgiften för @ScaledMetric är att tillhandahålla tillgänglighetsskalning av icke-textuella gränssnittselement. När användaren ökar teckenstorleken i iOS-inställningarna måste knappar, ikoner och mellanrum öka proportionellt för att gränssnittet ska förbli balanserat. @ScaledMetric löser denna uppgift automatiskt, utan manuell beräkning av multiplikatorer.

Syntax för @ScaledMetric

Grundläggande syntax @ScaledMetric använder ett standardvärde och en valfri parameter relativeTo. Om relativeTo anges binds skalningen till en specifik textstil (UIFontTextStyle). Om inte — används .body-skalan.

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

padding skalas i förhållande till .body (standard), iconSize — i förhållande till .title. Vid stor text ökar mellanrum och ikon proportionellt. Utan @ScaledMetric skulle mellanrum förbli 12 pt vid alla teckenstorlekar, vilket skulle leda till visuell obalans.

Hur @ScaledMetric fungerar

Mekanismen för @ScaledMetric är baserad på UIFontMetrics från UIKit. När SwiftUI skapar en @ScaledMetric-instans beräknar den en multiplikator baserat på aktuell preferred content size category (UIContentSizeCategory). Basvärdet multipliceras med scaledValue från UIFontMetrics för den angivna textstilen.

Matematiskt: ScaledMetricValue = baseValue × UIFontMetrics.scaledValue(for: relativeTo). Om relativeTo inte anges används UIFontMetrics.default kopplad till .body. När Dynamic Type ändras återskapar SwiftUI View-kroppen, @ScaledMetric beräknar det nya scaledValue-värdet och gränssnittet uppdateras automatiskt via mekanismen för @State-liknande PropertyWrappers.

Dynamic Type-skalningsskala

iOS-skalan omfattar 11 storlekar: från .extraSmall (5 pt) till .accessibilityExtraExtraExtraLarge (77 pt för .body). Skalningskoefficienten för .body varierar från 0.85 (XS) till 1.71 (XXXL) i förhållande till basvärdet. @ScaledMetric använder just denna skala, så värdet 12 pt för padding kan bli ~20 pt vid maximal tillgänglighetsstorlek.

Content Size CategoryKoefficient (body)Exempel @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

Val av relativeTo: använd .body för värden kopplade till huvudtexten (padding, spacing i listor), .title för stora element (iconSize, imageSize), .caption för små element (badge-storlek). Detta garanterar att element skalas i takt med omgivande text.

@ScaledMetric och Dynamic Type

Dynamic Type — iOS-funktion som låter användaren justera systemets teckenstorlek i Settings → Display & Brightness → Text Size. Ändringen tillämpas globalt på alla appar. @ScaledMetric reagerar automatiskt på denna förändring: SwiftUI uppdaterar alla @ScaledMetric-variabler när UIContentSizeCategory ändras.

Viktigt: @ScaledMetric skalar endast numeriska värden men hanterar inte teckensnitt direkt. För teckensnitt, använd .font() med textstil (.body, .title, .headline) — SwiftUI skalar teckensnittet automatiskt. @ScaledMetric kompletterar teckensnittsskalning för padding, spacing och elementstorlekar.

Kontroll av tillgänglighet via Canvas

Canvas Preview stöder Dynamic Type: i Canvas verktygsfält finns ett reglage Text Size (A–A) för att kontrollera gränssnittet vid olika teckenstorlekar. Använd det med @ScaledMetric för att säkerställa att mellanrum och storlekar skalas korrekt.

swift
struct CardView: View {
    @ScaledMetric private var cornerRadius: CGFloat = 16
    @ScaledMetric private var spacing: CGFloat = 8
    
    var body: some View {
        VStack(spacing: spacing) {
            Text("Korttitel")
                .font(.headline)
            Text("Beskrivning med dynamic type-stöd")
                .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 skalas från 16 pt till ~27 pt vid maximal tillgänglighetsstorlek. spacing — från 8 till ~14 pt. Detta garanterar att kortet förblir visuellt balanserat vid alla teckenstorlekar.

Exempel på @ScaledMetric

Exempel: ikon med Dynamic Type-stöd. Storleken på Image(systemName:)-ikoner skalas som standard inte under Dynamic Type. @ScaledMetric löser detta problem: genom att ändra imageScale eller ramstorlek baserat på aktuell skalningsfaktor.

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)
        }
    }
}

Exempel: tillgänglig badge-komponent. En badge med ett nummer bör skalas proportionellt med texten. @ScaledMetric för minimal badge-storlek garanterar att den runda badgen förblir synlig vid stor text.

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 skalar dessutom innehållet i Circle för att matcha den förstorade badgeSize. Utan fontScale skulle texten inuti badgen kanske inte få plats vid stor text.

@ScaledMetric vs @State — skillnader

@ScaledMetric och @State — båda property wrappers som spårar förändringar men med olika uppdateringskällor. @State uppdaterar värdet vid programmeringsändring (via $stateBinding). @ScaledMetric uppdaterar värdet automatiskt när systemets Dynamic Type ändras, men tillåter inte direkt ändring av värdet från kod.

Den viktigaste skillnaden: @ScaledMetric — read-only för utvecklaren och write-only för systemet. Du kan inte ändra scaledValue via setter — det beräknas av SwiftUI baserat på basvärdet och aktuell Dynamic Type. @State å andra sidan hanteras helt av utvecklaren. Om du behöver ett värde som både skalas under Dynamic Type och ändras programmatiskt — kombinera @ScaledMetric med @State eller använd en beräknad egenskap.

Egenskap@ScaledMetric@State
UppdateringskällaDynamic Type (system)Programmatiskt (utvecklare)
VärdetypCGFloat, Int, DoubleValfri
Ändring från kodKan inteKan via binding
Omritning av ViewNär Dynamic Type ändrasNär värdet ändras
iOS-versioniOS 14+iOS 13+

Kombinerat mönster: om du behöver ändra padding programmatiskt (t.ex. tryckanimering) och samtidigt skala under Dynamic Type, skapa en @ScaledMetric för det skalade basvärdet och en @State för animationsmultiplikatorn. Slutvärdet = scaledValue × animationMultiplier.

Vanliga misstag med @ScaledMetric

Misstag 1: använda @ScaledMetric för teckensnitt. @ScaledMetric skalar tal, inte teckensnitt. För teckensnitt, använd .font(.body) — SwiftUI tillämpar automatiskt Dynamic Type. Använd aldrig @ScaledMetric med font(.system(size: scaledSize)) — detta förstör systemets tillgänglighet.

Misstag 2: saknad relativeTo för olika element. Om du har padding (kopplad till .body) och iconSize (kopplad till .title), ange korrekt relativeTo för varje. Utan relativeTo skalas båda enligt .body, vilket leder till en oproportionerlig ökning av ikonen i förhållande till dess textuella sammanhang.

Misstag 3: @ScaledMetric i ViewModel/@ObservableObject. @ScaledMetric är en SwiftUI property wrapper som endast fungerar inuti en View. Den kan inte användas i ViewModel eller tjänster. För skalning i ViewModel, skicka det skalade värdet från View som parameter eller använd @Environment(\.sizeCategory) i View.

Hämta aktuell sizeCategory i kod

@Environment(\.sizeCategory) — alternativt sätt att hämta aktuell Dynamic Type i en View. Använd det när du behöver mer kontroll: beräkna en anpassad multiplikator, skicka sizeCategory till ViewModel eller kombinera med @ScaledMetric för flexibel skalning.

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("Anpassat skalat innehåll")
            .font(.body)
            .padding(basePadding + extraPadding)
    }
}

Extra mellanrum extraPadding läggs endast till vid tillgänglighetsstorlekar, vilket ger mer utrymme för stor text utan att ändra den grundläggande @ScaledMetric-logiken.

Vanliga frågor

Vad skiljer @ScaledMetric från @ScaledFont?

@ScaledMetric — officiell SwiftUI property wrapper för skalning av tal. @ScaledFont finns inte som standard-API — det är en anpassad wrapper implementerad av communityn. För teckensnitt, använd alltid inbyggd .font() med textstilar (.body, .title) och @ScaledMetric — för padding, spacing och storlekar.

Fungerar @ScaledMetric på watchOS och tvOS?

@ScaledMetric finns på iOS 14+, watchOS 7+, tvOS 14+ och macOS 11+. På watchOS är Dynamic Type begränsat till ett mindre intervall — storlekar från .extraSmall till .extraLarge utan tillgänglighetsstorlekar. På tvOS saknas Dynamic Type — @ScaledMetric returnerar alltid basvärdet.

Kan @ScaledMetric testas i enhetstester?

Ja, för att testa @ScaledMetric, skapa en View med @ScaledMetric och skicka environment-värdet .sizeCategory via .environment(\.sizeCategory, .extraExtraLarge). Hämta sedan elementets storlek via GeometryReader eller SwiftUI Inspector. Alternativt, kontrollera skalningslogiken via UIFontMetrics i en separat modul.

Hur interagerar @ScaledMetric med .dynamicTypeSize?

.dynamicTypeSize — en View-modifierare som begränsar maximal Dynamic Type för en hierarki (t.ex. .dynamicTypeSize(...large)). @ScaledMetric tar hänsyn till denna begränsning: om .dynamicTypeSize är inställd kommer det skalade värdet inte att överskrida motsvarande storlek. Kombinera båda API:erna för exakt kontroll.

Vad gör man om @ScaledMetric inte uppdaterar gränssnittet?

Se till att View använder @ScaledMetric inom sig (inte i ViewModel). Kontrollera att View prenumererar på Dynamic Type: @ScaledMetric utlöser automatiskt body-uppdatering, men om View använder .equatable() eller .id() kan mekanismen gå sönder. Använd @Environment(\.sizeCategory) som reserv.

Sammanfattning

  • @ScaledMetric — property wrapper SwiftUI för automatisk skalning av tal under Dynamic Type.
  • Bindning — relativeTo binder skalan till en specifik textstil (body, title, caption).
  • Accessibility — @ScaledMetric förbättrar gränssnittets tillgänglighet utan manuell kod.
  • Endast tal — wrappern skalar CGFloat, Int, Double men inte teckensnitt.
  • Intervall — från 0.85 (XS) till 1.71 (XXXL) i förhållande till basvärdet.
  • Read-only — @ScaledMetric kan inte ändras från kod, endast via systemet.

Vi utvecklar en mobil applikation nyckelfärdigt

IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.

Diskutera projektet

Läs också