@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, 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.
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.
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.
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.
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 Category | Koefficient (body) | Exempel @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 |
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.
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.
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.
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: 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.
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.
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 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älla | Dynamic Type (system) | Programmatiskt (utvecklare) |
| Värdetyp | CGFloat, Int, Double | Valfri |
| Ändring från kod | Kan inte | Kan via binding |
| Omritning av View | När Dynamic Type ändras | När värdet ändras |
| iOS-version | iOS 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.
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.
@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.
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
@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.
@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.
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.
.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.
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
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.
Läs också