@ScaledMetric — wat is het, property wrapper SwiftUI en Dynamic Type

Auteur: IT Sectr Gepubliceerd: 2026-06-27 Leestijd: 10 min

@ScaledMetric — property wrapper SwiftUI die automatisch een numerieke waarde schaalt volgens de Dynamic Type-instellingen van de gebruiker. De waarde wordt ingepakt in @ScaledMetric en herberekend bij wijziging van de systeemlettergrootte, wat de toegankelijkheid van de interface garandeert voor mensen met een visuele beperking. Volgens Apple Developer Documentation (2026) gebruikt @ScaledMetric de UIFontMetrics-schaal om de relatieve schaal te berekenen op basis van preferred content size category. Lees meer over accessibility in het artikel over accessibility in SwiftUI.

Belangrijkste punten

  • @ScaledMetric — property wrapper SwiftUI voor het schalen van waarden onder Dynamic Type.
  • Dynamic Type — systeeminstelling van iOS die de lettergrootte wijzigt via UIFontTextStyle.
  • Schaling — @ScaledMetric accepteert een basiswaarde en de relativeTo-multiplier.
  • Automatische update — bij wijziging van Dynamic Type wordt @ScaledMetric herberekend en de UI bijgewerkt.
  • Accessibility — gebruik van @ScaledMetric verbetert de toegankelijkheid van de interface zonder extra code.

Wat is @ScaledMetric?

@ScaledMetric — property wrapper SwiftUI, toegevoegd in iOS 14, die automatisch een numerieke waarde (CGFloat, Int, Double) schaalt naar de huidige Dynamic Type-lettergrootte. In tegenstelling tot .font(.body) voor lettertypen, schaalt @ScaledMetric alle numerieke parameters: padding, spacing, cornerRadius, iconSize — alles wat proportioneel moet toenemen bij grote tekst.

De hoofdtaak van @ScaledMetric is het zorgen voor accessibility-schaling van niet-tekstuele interface-elementen. Wanneer de gebruiker het lettertype vergroot in de iOS-instellingen, moeten knoppen, pictogrammen en tussenruimten proportioneel toenemen om de interface in balans te houden. @ScaledMetric lost deze taak automatisch op, zonder handmatige berekening van multipliers.

Syntax van @ScaledMetric

Basis syntax @ScaledMetric gebruikt een standaardwaarde en een optionele parameter relativeTo. Als relativeTo is opgegeven, wordt de schaling gekoppeld aan een specifieke tekststijl (UIFontTextStyle). Zo niet — wordt de .body-schaal gebruikt.

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

padding wordt geschaald ten opzichte van .body (standaard), iconSize — ten opzichte van .title. Bij grote tekst zullen tussenruimten en pictogram proportioneel toenemen. Zonder @ScaledMetric zouden tussenruimten 12 pt blijven bij elke lettergrootte, wat zou leiden tot visuele disbalans.

Hoe werkt @ScaledMetric

Het mechanisme van @ScaledMetric is gebaseerd op UIFontMetrics uit UIKit. Wanneer SwiftUI een @ScaledMetric-instantie maakt, berekent het een multiplier op basis van de huidige preferred content size category (UIContentSizeCategory). De basiswaarde wordt vermenigvuldigd met scaledValue van UIFontMetrics voor de opgegeven tekststijl.

Wiskundig: ScaledMetricValue = baseValue × UIFontMetrics.scaledValue(for: relativeTo). Als relativeTo niet is opgegeven, wordt UIFontMetrics.default gebruikt, gekoppeld aan .body. Bij wijziging van Dynamic Type maakt SwiftUI de body van de View opnieuw aan, berekent @ScaledMetric de nieuwe scaledValue en wordt de UI automatisch bijgewerkt via het mechanisme van @State-achtige PropertyWrappers.

De schalingsschaal van Dynamic Type

De iOS-schaal omvat 11 groottes: van .extraSmall (5 pt) tot .accessibilityExtraExtraExtraLarge (77 pt voor .body). De schalingsfactor voor .body varieert van 0.85 (XS) tot 1.71 (XXXL) ten opzichte van de basiswaarde. @ScaledMetric gebruikt precies deze schaal, dus een waarde van 12 pt voor padding kan ~20 pt worden bij de maximale accessibility-grootte.

Content Size CategoryFactor (body)Voorbeeld @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

Keuze van relativeTo: gebruik .body voor waarden die verband houden met de hoofdtekst (padding, spacing in lijsten), .title voor grote elementen (iconSize, imageSize), .caption voor kleine elementen (badge-grootte). Dit garandeert dat elementen in de pas schalen met de omringende tekst.

@ScaledMetric en Dynamic Type

Dynamic Type — iOS-functie waarmee de gebruiker de systeemlettergrootte kan aanpassen in Settings → Display & Brightness → Text Size. De wijziging wordt wereldwijd toegepast op alle apps. @ScaledMetric reageert automatisch op deze wijziging: SwiftUI werkt alle @ScaledMetric-variabelen bij bij verandering van UIContentSizeCategory.

Belangrijk: @ScaledMetric schaalt alleen numerieke waarden, maar beheert niet direct lettertypen. Gebruik voor lettertypen .font() met een tekststijl (.body, .title, .headline) — SwiftUI schaalt het lettertype automatisch. @ScaledMetric vult de letterschaling aan voor padding, spacing en elementgroottes.

Toegankelijkheid controleren via Canvas

Canvas Preview ondersteunt Dynamic Type: in de Canvas-werkbalk is er een schuifregelaar Text Size (A–A) om de UI te controleren bij verschillende lettergroottes. Gebruik het met @ScaledMetric om er zeker van te zijn dat tussenruimten en groottes correct schalen.

swift
struct CardView: View {
    @ScaledMetric private var cornerRadius: CGFloat = 16
    @ScaledMetric private var spacing: CGFloat = 8
    
    var body: some View {
        VStack(spacing: spacing) {
            Text("Kaarttitel")
                .font(.headline)
            Text("Beschrijving met dynamic type-ondersteuning")
                .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 schaalt van 16 pt tot ~27 pt bij maximale accessibility-grootte. spacing — van 8 tot ~14 pt. Dit garandeert dat de kaart visueel in balans blijft bij elke lettergrootte.

Voorbeelden van @ScaledMetric

Voorbeeld: pictogram met Dynamic Type-ondersteuning. De grootte van Image(systemName:)-pictogrammen wordt standaard niet geschaald onder Dynamic Type. @ScaledMetric lost dit probleem op: door imageScale of framegrootte te wijzigen op basis van de huidige schalingsfactor.

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

Voorbeeld: toegankelijke badge-component. Een badge met een getal moet proportioneel schalen met de tekst. @ScaledMetric voor de minimale badge-grootte garandeert dat de ronde badge zichtbaar blijft bij groot lettertype.

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 schaalt bovendien de inhoud van de Circle om aan te passen aan de vergrote badgeSize. Zonder fontScale zou de tekst in de badge mogelijk niet passen bij groot lettertype.

@ScaledMetric vs @State — verschillen

@ScaledMetric en @State — beide property wrappers die wijzigingen volgen, maar met verschillende updateronnen. @State werkt de waarde bij bij programmatische wijziging (via $stateBinding). @ScaledMetric werkt de waarde automatisch bij bij wijziging van systeem Dynamic Type, maar staat geen directe wijziging van de waarde uit code toe.

Het belangrijkste verschil: @ScaledMetric — read-only voor de ontwikkelaar en write-only voor het systeem. U kunt scaledValue niet wijzigen via een setter — het wordt berekend door SwiftUI op basis van de basiswaarde en de huidige Dynamic Type. @State daarentegen wordt volledig beheerd door de ontwikkelaar. Als u een waarde nodig hebt die zowel onder Dynamic Type schaalt als programmatisch wijzigbaar is — combineer dan @ScaledMetric met @State of gebruik een berekende eigenschap.

Kenmerk@ScaledMetric@State
UpdatebronDynamic Type (systeem)Programmatisch (ontwikkelaar)
WaardetypeCGFloat, Int, DoubleElk type
Wijziging uit codeNiet mogelijkWel via binding
View hertekenenBij wijziging Dynamic TypeBij wijziging waarde
iOS-versieiOS 14+iOS 13+

Gecombineerd patroon: als u padding programmatisch moet wijzigen (bijv. animatie bij indrukken) en tegelijkertijd moet schalen onder Dynamic Type, maak dan een @ScaledMetric voor de geschaalde basiswaarde en een @State voor de animatiemultiplier. De uiteindelijke waarde = scaledValue × animationMultiplier.

Veelvoorkomende fouten met @ScaledMetric

Fout 1: @ScaledMetric gebruiken voor lettertypen. @ScaledMetric schaalt getallen, geen lettertypen. Gebruik voor lettertypen .font(.body) — SwiftUI past automatisch Dynamic Type toe. Gebruik nooit @ScaledMetric met font(.system(size: scaledSize)) — dit verbreekt de systeem-accessibility.

Fout 2: ontbreken van relativeTo voor verschillende elementen. Als u padding (gekoppeld aan .body) en iconSize (gekoppeld aan .title) hebt, geef dan voor elk de juiste relativeTo op. Zonder relativeTo worden beide geschaald volgens .body, wat leidt tot een disproportionele toename van het pictogram ten opzichte van de tekstuele context.

Fout 3: @ScaledMetric in ViewModel/@ObservableObject. @ScaledMetric is een SwiftUI property wrapper die alleen binnen een View werkt. Het kan niet worden gebruikt in ViewModel of services. Voor schaling in ViewModel, geef de geschaalde waarde uit de View door als parameter of gebruik @Environment(\.sizeCategory) in de View.

Huidige sizeCategory ophalen in code

@Environment(\.sizeCategory) — alternatieve manier om de huidige Dynamic Type in een View te verkrijgen. Gebruik het wanneer u meer controle nodig hebt: een aangepaste multiplier berekenen, sizeCategory doorgeven aan ViewModel of combineren met @ScaledMetric voor flexibele schaling.

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("Aangepaste geschaalde inhoud")
            .font(.body)
            .padding(basePadding + extraPadding)
    }
}

Extra tussenruimte extraPadding wordt alleen toegevoegd bij accessibility-groottes, waardoor er meer ruimte komt voor grote tekst zonder de basis @ScaledMetric-logica te wijzigen.

Veelgestelde vragen

Waarin verschilt @ScaledMetric van @ScaledFont?

@ScaledMetric — de officiële SwiftUI property wrapper voor het schalen van getallen. @ScaledFont bestaat niet als standaard API — het is een aangepaste wrapper geïmplementeerd door de community. Gebruik voor lettertypen altijd de ingebouwde .font() met tekststijlen (.body, .title) en @ScaledMetric — voor padding, spacing en groottes.

Werkt @ScaledMetric op watchOS en tvOS?

@ScaledMetric is beschikbaar op iOS 14+, watchOS 7+, tvOS 14+ en macOS 11+. Op watchOS is Dynamic Type beperkt tot een kleiner bereik — groottes van .extraSmall tot .extraLarge zonder accessibility-groottes. Op tvOS ontbreekt Dynamic Type — @ScaledMetric retourneert altijd de basiswaarde.

Kan @ScaledMetric worden getest in unit-tests?

Ja, om @ScaledMetric te testen, maakt u een View met @ScaledMetric en geeft u de environment-waarde .sizeCategory door via .environment(\.sizeCategory, .extraExtraLarge). Verkrijg vervolgens de elementgrootte via GeometryReader of SwiftUI Inspector. Controleer alternatief de schalingslogica via UIFontMetrics in een aparte module.

Hoe interageert @ScaledMetric met .dynamicTypeSize?

.dynamicTypeSize — een View-modifier die de maximale Dynamic Type voor een hiërarchie beperkt (bijv. .dynamicTypeSize(...large)). @ScaledMetric houdt rekening met deze beperking: als .dynamicTypeSize is ingesteld, zal de geschaalde waarde de corresponderende grootte niet overschrijden. Combineer beide API's voor nauwkeurige controle.

Wat te doen als @ScaledMetric de UI niet bijwerkt?

Zorg ervoor dat de View @ScaledMetric in zichzelf gebruikt (niet in ViewModel). Controleer of de View abonneert op Dynamic Type: @ScaledMetric roept automatisch body-refresh aan, maar als de View .equatable() of .id() gebruikt, kan het mechanisme breken. Gebruik @Environment(\.sizeCategory) als fallback.

Samenvatting

  • @ScaledMetric — property wrapper SwiftUI voor automatische schaling van getallen onder Dynamic Type.
  • Koppeling — relativeTo koppelt de schaal aan een specifieke tekststijl (body, title, caption).
  • Accessibility — @ScaledMetric verbetert de toegankelijkheid van de interface zonder handmatige code.
  • Alleen getallen — de wrapper schaalt CGFloat, Int, Double, maar geen lettertypen.
  • Bereik — van 0.85 (XS) tot 1.71 (XXXL) ten opzichte van de basiswaarde.
  • Read-only — @ScaledMetric kan niet worden gewijzigd uit code, alleen via het systeem.

We ontwikkelen een mobiele applicatie turnkey

IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.

Bespreek het project

Lees ook