@ScaledMetric — τι είναι, property wrapper SwiftUI και Dynamic Type

Συγγραφέας: IT Sectr Δημοσιεύτηκε: 2026-06-27 Χρόνος ανάγνωσης: 10 λεπ

@ScaledMetric — property wrapper SwiftUI που κλιμακώνει αυτόματα την αριθμητική τιμή σύμφωνα με τις ρυθμίσεις Dynamic Type του χρήστη. Η τιμή τυλίγεται στο @ScaledMetric και επαναϋπολογίζεται όταν αλλάζει το μέγεθος συστήματος γραμματοσειράς, εξασφαλίζοντας την προσβασιμότητα της διεπαφής για άτομα με προβλήματα όρασης. Σύμφωνα με το Apple Developer Documentation (2026), το @ScaledMetric χρησιμοποιεί την κλίμακα UIFontMetrics για τον υπολογισμό της σχετικής κλίμακας βάσει του preferred content size category. Διαβάστε περισσότερα για την προσβασιμότητα στο υλικό για την προσβασιμότητα του SwiftUI.

Κύρια σημεία

  • @ScaledMetric — property wrapper SwiftUI για κλιμάκωση τιμών υπό Dynamic Type.
  • Dynamic Type — ρύθμιση συστήματος iOS που αλλάζει το μέγεθος γραμματοσειράς από UIFontTextStyle.
  • Κλιμάκωση — το @ScaledMetric δέχεται βασική τιμή και πολλαπλασιαστή relativeTo.
  • Αυτόματη ενημέρωση — όταν αλλάζει το Dynamic Type, το @ScaledMetric επαναϋπολογίζεται και το UI ενημερώνεται.
  • Accessibility — η χρήση του @ScaledMetric βελτιώνει την προσβασιμότητα της διεπαφής χωρίς επιπλέον κώδικα.

Τι είναι το @ScaledMetric;

@ScaledMetric — property wrapper SwiftUI, που προστέθηκε στο iOS 14, το οποίο κλιμακώνει αυτόματα την αριθμητική τιμή (CGFloat, Int, Double) στο τρέχον μέγεθος γραμματοσειράς Dynamic Type. Σε αντίθεση με το .font(.body) για γραμματοσειρές, το @ScaledMetric κλιμακώνει οποιεσδήποτε αριθμητικές παραμέτρους: padding, spacing, cornerRadius, iconSize — ό,τι πρέπει να αυξάνεται αναλογικά σε μεγάλο κείμενο.

Η κύρια αποστολή του @ScaledMetric είναι να παρέχει κλιμάκωση προσβασιμότητας μη-κειμενικών στοιχείων διεπαφής. Όταν ο χρήστης μεγαλώνει τη γραμματοσειρά στις ρυθμίσεις iOS, τα κουμπιά, τα εικονίδια και τα διαστήματα πρέπει να αυξάνονται αναλογικά για να παραμένει ισορροπημένη η διεπαφή. Το @ScaledMetric λύνει αυτό το έργο αυτόματα, χωρίς χειροκίνητο υπολογισμό πολλαπλασιαστών.

Σύνταξη του @ScaledMetric

Βασική σύνταξη Το @ScaledMetric χρησιμοποιεί προεπιλεγμένη τιμή και προαιρετική παράμετρο relativeTo. Αν το relativeTo έχει οριστεί, η κλιμάκωση συνδέεται με συγκεκριμένο στυλ κειμένου (UIFontTextStyle). Αν όχι — χρησιμοποιείται η κλίμακα .body.

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

padding θα κλιμακωθεί σε σχέση με το .body (προεπιλογή), iconSize — σε σχέση με το .title. Σε μεγάλο κείμενο, τα διαστήματα και το εικονίδιο θα αυξηθούν αναλογικά. Χωρίς @ScaledMetric, τα διαστήματα θα παρέμεναν 12 pt σε οποιοδήποτε μέγεθος γραμματοσειράς, οδηγώντας σε οπτική ανισορροπία.

Πώς λειτουργεί το @ScaledMetric

Ο μηχανισμός του @ScaledMetric βασίζεται στο UIFontMetrics από το UIKit. Όταν το SwiftUI δημιουργεί μια παρουσία @ScaledMetric, υπολογίζει έναν πολλαπλασιαστή βάσει της τρέχουσας preferred content size category (UIContentSizeCategory). Η βασική τιμή πολλαπλασιάζεται με το scaledValue από το UIFontMetrics για το καθορισμένο στυλ κειμένου.

Μαθηματικά: ScaledMetricValue = baseValue × UIFontMetrics.scaledValue(for: relativeTo). Αν το relativeTo δεν έχει οριστεί, χρησιμοποιείται το UIFontMetrics.default συνδεδεμένο με το .body. Όταν αλλάζει το Dynamic Type, το SwiftUI αναδημιουργεί το σώμα του View, το @ScaledMetric υπολογίζει το νέο scaledValue και το UI ενημερώνεται αυτόματα μέσω του μηχανισμού PropertyWrappers που μοιάζει με @State.

Κλίμακα κλιμάκωσης Dynamic Type

Η κλίμακα iOS περιλαμβάνει 11 μεγέθη: από .extraSmall (5 pt) έως .accessibilityExtraExtraExtraLarge (77 pt για .body). Ο συντελεστής κλιμάκωσης για .body κυμαίνεται από 0.85 (XS) έως 1.71 (XXXL) σε σχέση με τη βασική τιμή. Το @ScaledMetric χρησιμοποιεί ακριβώς αυτήν την κλίμακα, επομένως η τιμή 12 pt για padding μπορεί να γίνει ~20 pt στο μέγιστο μέγεθος προσβασιμότητας.

Content Size CategoryΣυντελεστής (body)Παράδειγμα @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: χρησιμοποιήστε .body για τιμές που σχετίζονται με το κύριο κείμενο (padding, spacing σε λίστες), .title για μεγάλα στοιχεία (iconSize, imageSize), .caption για μικρά στοιχεία (μέγεθος badge). Αυτό εξασφαλίζει ότι τα στοιχεία κλιμακώνονται σε αρμονία με το περιβάλλον κείμενο.

@ScaledMetric και Dynamic Type

Dynamic Type — λειτουργία iOS που επιτρέπει στο χρήστη να προσαρμόσει το μέγεθος γραμματοσειράς συστήματος στις Settings → Display & Brightness → Text Size. Η αλλαγή εφαρμόζεται καθολικά σε όλες τις εφαρμογές. Το @ScaledMetric αντιδρά αυτόματα σε αυτήν την αλλαγή: το SwiftUI ενημερώνει όλες τις @ScaledMetric μεταβλητές όταν αλλάζει το UIContentSizeCategory.

Σημαντικό: Το @ScaledMetric κλιμακώνει μόνο αριθμητικές τιμές, αλλά δεν διαχειρίζεται άμεσα γραμματοσειρές. Για γραμματοσειρές χρησιμοποιήστε το .font() με στυλ κειμένου (.body, .title, .headline) — το SwiftUI κλιμακώνει αυτόματα τη γραμματοσειρά. Το @ScaledMetric συμπληρώνει την κλιμάκωση γραμματοσειράς για padding, spacing και μεγέθη στοιχείων.

Έλεγχος προσβασιμότητας μέσω Canvas

Canvas Preview υποστηρίζει Dynamic Type: στη γραμμή εργαλείων Canvas υπάρχει ένα ρυθμιστικό Text Size (A–A) για έλεγχο του UI σε διαφορετικά μεγέθη γραμματοσειράς. Χρησιμοποιήστε το με @ScaledMetric για να βεβαιωθείτε ότι τα διαστήματα και τα μεγέθη κλιμακώνονται σωστά.

swift
struct CardView: View {
    @ScaledMetric private var cornerRadius: CGFloat = 16
    @ScaledMetric private var spacing: CGFloat = 8
    
    var body: some View {
        VStack(spacing: spacing) {
            Text("Τίτλος κάρτας")
                .font(.headline)
            Text("Περιγραφή με υποστήριξη 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 κλιμακώνεται από 16 pt έως ~27 pt στο μέγιστο μέγεθος προσβασιμότητας. spacing — από 8 έως ~14 pt. Αυτό εξασφαλίζει ότι η κάρτα παραμένει οπτικά ισορροπημένη σε οποιοδήποτε μέγεθος γραμματοσειράς.

Παραδείγματα @ScaledMetric

Παράδειγμα: εικονίδιο με υποστήριξη Dynamic Type. Το μέγεθος εικονιδίων Image(systemName:) από προεπιλογή δεν κλιμακώνεται υπό Dynamic Type. Το @ScaledMetric λύνει αυτό το πρόβλημα: αλλάζοντας το imageScale ή το μέγεθος frame βάσει του τρέχοντος συντελεστή κλίμακας.

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

Παράδειγμα: προσβάσιμο στοιχείο badge. Το badge με αριθμό πρέπει να κλιμακώνεται αναλογικά με το κείμενο. Το @ScaledMetric για το ελάχιστο μέγεθος badge εξασφαλίζει ότι το κυκλικό badge παραμένει ορατό σε μεγάλη γραμματοσειρά.

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 επιπλέον κλιμακώνει το περιεχόμενο του Circle για να ταιριάζει με το αυξημένο badgeSize. Χωρίς fontScale, το κείμενο μέσα στο badge μπορεί να μην χωράει σε μεγάλη γραμματοσειρά.

@ScaledMetric vs @State — διαφορές

@ScaledMetric και @State — και τα δύο property wrappers που παρακολουθούν αλλαγές, αλλά με διαφορετικές πηγές ενημέρωσης. Το @State ενημερώνει την τιμή σε προγραμματιστική αλλαγή (μέσω $stateBinding). Το @ScaledMetric ενημερώνει την τιμή αυτόματα όταν αλλάζει το σύστημα Dynamic Type, αλλά δεν επιτρέπει άμεση αλλαγή της τιμής από τον κώδικα.

Η βασική διαφορά: @ScaledMetric — read-only για τον προγραμματιστή και write-only για το σύστημα. Δεν μπορείτε να αλλάξετε το scaledValue μέσω setter — υπολογίζεται από το SwiftUI βάσει της βασικής τιμής και του τρέχοντος Dynamic Type. Το @State, αντίθετα, ελέγχεται πλήρως από τον προγραμματιστή. Εάν χρειάζεστε μια τιμή που και κλιμακώνεται υπό Dynamic Type και αλλάζει προγραμματιστικά — συνδυάστε @ScaledMetric με @State ή χρησιμοποιήστε υπολογιζόμενη ιδιότητα.

Χαρακτηριστικό@ScaledMetric@State
Πηγή ενημέρωσηςDynamic Type (σύστημα)Προγραμματιστικά (προγραμματιστής)
Τύπος τιμήςCGFloat, Int, DoubleΟποιοδήποτε
Αλλαγή από κώδικαΔεν γίνεταιΓίνεται μέσω binding
Επανασχεδίαση ViewΌταν αλλάζει Dynamic TypeΌταν αλλάζει η τιμή
Έκδοση iOSiOS 14+iOS 13+

Συνδυασμένο μοτίβο: αν χρειάζεται να αλλάξετε το padding προγραμματιστικά (π.χ. κινούμενο σχέδιο πατήματος) και ταυτόχρονα να κλιμακώσετε υπό Dynamic Type, δημιουργήστε ένα @ScaledMetric για τη βασική κλιμακούμενη τιμή και ένα @State για τον πολλαπλασιαστή κίνησης. Η τελική τιμή = scaledValue × animationMultiplier.

Συνηθισμένα λάθη με το @ScaledMetric

Λάθος 1: χρήση @ScaledMetric για γραμματοσειρές. Το @ScaledMetric κλιμακώνει αριθμούς, όχι γραμματοσειρές. Για γραμματοσειρές χρησιμοποιήστε το .font(.body) — το SwiftUI εφαρμόζει αυτόματα Dynamic Type. Ποτέ μην χρησιμοποιείτε @ScaledMetric με font(.system(size: scaledSize)) — αυτό χαλάει την προσβασιμότητα συστήματος.

Λάθος 2: έλλειψη relativeTo για ετερογενή στοιχεία. Αν έχετε padding (σχετικό με .body) και iconSize (σχετικό με .title), καθορίστε το σωστό relativeTo για κάθε ένα. Χωρίς relativeTo, και τα δύο θα κλιμακωθούν σύμφωνα με το .body, οδηγώντας σε δυσανάλογη αύξηση του εικονιδίου σε σχέση με το κειμενικό του πλαίσιο.

Λάθος 3: @ScaledMetric σε ViewModel/@ObservableObject. Το @ScaledMetric είναι property wrapper SwiftUI που λειτουργεί μόνο μέσα σε View. Δεν μπορεί να χρησιμοποιηθεί σε ViewModel ή υπηρεσίες. Για κλιμάκωση σε ViewModel, μεταφέρετε την κλιμακούμενη τιμή από το View ως παράμετρο ή χρησιμοποιήστε @Environment(\.sizeCategory) στο View.

Λήψη τρέχουσας sizeCategory σε κώδικα

@Environment(\.sizeCategory) — εναλλακτική μέθοδος λήψης του τρέχοντος Dynamic Type στο View. Χρησιμοποιήστε το όταν χρειάζεστε περισσότερο έλεγχο: υπολογισμό προσαρμοσμένου πολλαπλασιαστή, μεταφορά sizeCategory στο ViewModel ή συνδυασμό με @ScaledMetric για ευέλικτη κλιμάκωση.

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("Προσαρμοσμένο κλιμακούμενο περιεχόμενο")
            .font(.body)
            .padding(basePadding + extraPadding)
    }
}

Επιπλέον διάστημα extraPadding προστίθεται μόνο σε μεγέθη προσβασιμότητας, δίνοντας περισσότερο χώρο για μεγάλο κείμενο χωρίς αλλαγή της βασικής @ScaledMetric λογικής.

Συχνές ερωτήσεις

Σε τι διαφέρει το @ScaledMetric από το @ScaledFont;

@ScaledMetric — επίσημο property wrapper SwiftUI για κλιμάκωση αριθμών. Το @ScaledFont δεν υπάρχει ως standard API — είναι ένα προσαρμοσμένο wrapper που υλοποιήθηκε από την κοινότητα. Για γραμματοσειρές χρησιμοποιείτε πάντα το ενσωματωμένο .font() με στυλ κειμένου (.body, .title) και @ScaledMetric — για padding, spacing και μεγέθη.

Λειτουργεί το @ScaledMetric σε watchOS και tvOS;

@ScaledMetric είναι διαθέσιμο σε iOS 14+, watchOS 7+, tvOS 14+ και macOS 11+. Στο watchOS, το Dynamic Type περιορίζεται σε μικρότερο εύρος — μεγέθη από .extraSmall έως .extraLarge χωρίς μεγέθη προσβασιμότητας. Στο tvOS, το Dynamic Type απουσιάζει — το @ScaledMetric επιστρέφει πάντα τη βασική τιμή.

Μπορεί να δοκιμαστεί το @ScaledMetric σε unit-tests;

Ναι, για δοκιμή του @ScaledMetric δημιουργήστε ένα View με @ScaledMetric και μεταφέρετε την environment τιμή .sizeCategory μέσω .environment(\.sizeCategory, .extraExtraLarge). Στη συνέχεια, λάβετε το μέγεθος στοιχείου μέσω GeometryReader ή SwiftUI Inspector. Εναλλακτικά, ελέγξτε τη λογική κλιμάκωσης μέσω UIFontMetrics σε ξεχωριστή ενότητα.

Πώς αλληλεπιδρά το @ScaledMetric με το .dynamicTypeSize;

.dynamicTypeSize — modifier View που περιορίζει το μέγιστο Dynamic Type για μια ιεραρχία (π.χ. .dynamicTypeSize(...large)). Το @ScaledMetric λαμβάνει υπόψη αυτόν τον περιορισμό: αν το .dynamicTypeSize έχει οριστεί, η κλιμακούμενη τιμή δεν θα υπερβεί το αντίστοιχο μέγεθος. Συνδυάστε και τα δύο API για ακριβή έλεγχο.

Τι να κάνετε αν το @ScaledMetric δεν ενημερώνει το UI;

Βεβαιωθείτε ότι το View χρησιμοποιεί το @ScaledMetric μέσα του (όχι σε ViewModel). Ελέγξτε ότι το View είναι συνδρομημένο στο Dynamic Type: το @ScaledMetric ενεργοποιεί αυτόματα ανανέωση body, αλλά αν το View χρησιμοποιεί .equatable() ή .id(), ο μηχανισμός μπορεί να σπάσει. Χρησιμοποιήστε το @Environment(\.sizeCategory) ως fallback.

Σύνοψη

  • @ScaledMetric — property wrapper SwiftUI για αυτόματη κλιμάκωση αριθμών υπό Dynamic Type.
  • Σύνδεση — το relativeTo συνδέει την κλίμακα με συγκεκριμένο στυλ κειμένου (body, title, caption).
  • Accessibility — το @ScaledMetric βελτιώνει την προσβασιμότητα της διεπαφής χωρίς χειροκίνητο κώδικα.
  • Μόνο αριθμοί — το wrapper κλιμακώνει CGFloat, Int, Double, αλλά όχι γραμματοσειρές.
  • Εύρος — από 0.85 (XS) έως 1.71 (XXXL) σε σχέση με τη βασική τιμή.
  • Read-only — το @ScaledMetric δεν μπορεί να αλλάξει από κώδικα, μόνο μέσω συστήματος.

Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση

Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.

Συζήτηση έργου

Διαβάστε επίσης