@ScaledMetric — nedir, SwiftUI property wrapper ve Dynamic Type

Yazar: IT Sectr Yayınlanma: 2026-06-27 Okuma süresi: 10 dk

@ScaledMetric, kullanıcının Dynamic Type ayarlarına göre sayısal bir değeri otomatik olarak ölçeklendiren bir SwiftUI property wrapper'ıdır. Değer @ScaledMetric içine sarılır ve sistem yazı tipi boyutu değiştiğinde yeniden hesaplanır, böylece görme engelli kişiler için arayüz erişilebilirliği sağlanır. Apple Developer Documentation'a (2026) göre @ScaledMetric, tercih edilen içerik boyutu kategorisine dayalı olarak göreceli ölçeği hesaplamak için UIFontMetrics ölçeğini kullanır. Erişilebilirlik hakkında daha fazla bilgi için SwiftUI erişilebilirlik makalesine göz atın.

Anahtar noktalar

  • @ScaledMetric — Dynamic Type'a göre değerleri ölçeklendiren bir SwiftUI property wrapper'ı.
  • Dynamic Type — UIFontTextStyle'den yazı tipi boyutunu değiştiren bir iOS sistem ayarı.
  • Ölçekleme — @ScaledMetric bir temel değer ve bir relativeTo çarpanı alır.
  • Otomatik güncelleme — Dynamic Type değiştiğinde @ScaledMetric yeniden hesaplar ve arayüz güncellenir.
  • Erişilebilirlik — @ScaledMetric kullanmak, ek kod olmadan arayüz erişilebilirliğini artırır.

@ScaledMetric nedir?

@ScaledMetric, iOS 14'te eklenen, sayısal bir değeri (CGFloat, Int, Double) geçerli Dynamic Type yazı tipi boyutuna otomatik olarak ölçeklendiren bir SwiftUI property wrapper'ıdır. Yazı tipleri için .font(.body)'nin aksine, @ScaledMetric herhangi bir sayısal parametreyi ölçeklendirir: padding, spacing, cornerRadius, iconSize — daha büyük metinle orantılı olarak artması gereken her şey.

@ScaledMetric'in temel amacı, metin olmayan arayüz öğeleri için erişilebilirlik ölçeklemesi sağlamaktır. Kullanıcı iOS ayarlarında yazı tipi boyutunu artırdığında, düğmeler, simgeler ve boşluklar arayüzü dengede tutmak için orantılı olarak ölçeklenmelidir. @ScaledMetric, manuel çarpan hesaplaması olmadan bunu otomatik olarak halleder.

@ScaledMetric sözdizimi

Temel sözdizimi @ScaledMetric, varsayılan bir değer ve isteğe bağlı bir relativeTo parametresi kullanır. relativeTo belirtilirse, ölçekleme belirli bir metin stiline (UIFontTextStyle) bağlanır. Belirtilmezse, .body ölçeği kullanılır.

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

padding, .body'ye (varsayılan) göre ölçeklenecek, iconSize ise .title'a göre ölçeklenecektir. Daha büyük metinle, boşluklar ve simgeler orantılı olarak artacaktır. @ScaledMetric olmadan, padding yazı tipi boyutundan bağımsız olarak 12 pt'de kalır ve görsel dengesizliğe neden olur.

@ScaledMetric nasıl çalışır

@ScaledMetric mekanizması, UIKit'ten UIFontMetrics'e dayanır. SwiftUI bir @ScaledMetric örneği oluşturduğunda, geçerli tercih edilen içerik boyutu kategorisine (UIContentSizeCategory) dayalı olarak bir çarpan hesaplar. Temel değer, belirtilen metin stili için UIFontMetrics'ten scaledValue ile çarpılır.

Matematiksel olarak: ScaledMetricValue = baseValue × UIFontMetrics.scaledValue(for: relativeTo). relativeTo belirtilmezse, .body'ye bağlı UIFontMetrics.default kullanılır. Dynamic Type değiştiğinde, SwiftUI View gövdesini yeniden oluşturur, @ScaledMetric yeni bir scaledValue hesaplar ve arayüz, @State benzeri PropertyWrappers mekanizması aracılığıyla otomatik olarak güncellenir.

Dynamic Type ölçekleme skalası

iOS skalası 11 boyut içerir: .extraSmall (5 pt)'den .accessibilityExtraExtraExtraLarge (.body için 77 pt)'ye kadar. .body için ölçekleme faktörü, temel değere göre 0.85 (XS) ile 1.71 (XXXL) arasında değişir. @ScaledMetric tam olarak bu ölçeği kullanır, bu nedenle 12 pt'lik bir padding değeri maksimum erişilebilirlik boyutunda ~20 pt olabilir.

İçerik boyutu kategorisiFaktör (body)Örnek @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 seçimi: gövde metniyle ilgili değerler için (listelerde padding, spacing) .body, büyük öğeler için (iconSize, imageSize) .title, küçük öğeler için (rozet boyutu) .caption kullanın. Bu, öğelerin çevreleyen metinle senkronize bir şekilde ölçeklenmesini sağlar.

@ScaledMetric ve Dynamic Type

Dynamic Type, kullanıcıların Ayarlar → Ekran ve Parlaklık → Metin Boyutu'nda sistem yazı tipi boyutunu ayarlamasına olanak tanıyan bir iOS özelliğidir. Değişiklik tüm uygulamalara küresel olarak uygulanır. @ScaledMetric bu değişikliğe otomatik olarak tepki verir: UIContentSizeCategory değiştiğinde SwiftUI tüm @ScaledMetric değişkenlerini günceller.

Önemli: @ScaledMetric yalnızca sayısal değerleri ölçeklendirir, yazı tiplerini doğrudan yönetmez. Yazı tipleri için bir metin stiliyle (.body, .title, .headline) .font() kullanın — SwiftUI yazı tipini otomatik olarak ölçeklendirir. @ScaledMetric, yazı tipi ölçeklemeyi padding, spacing ve öğe boyutları için tamamlar.

Canvas aracılığıyla erişilebilirlik testi

Canvas Preview, Dynamic Type'ı destekler: Canvas araç çubuğunda, farklı yazı tipi boyutlarında arayüzü test etmek için bir Metin Boyutu kaydırıcısı (A–A) bulunur. Boşlukların ve boyutların doğru şekilde ölçeklendiğini doğrulamak için @ScaledMetric ile birlikte kullanın.

swift
struct CardView: View {
    @ScaledMetric private var cornerRadius: CGFloat = 16
    @ScaledMetric private var spacing: CGFloat = 8
    
    var body: some View {
        VStack(spacing: spacing) {
            Text("Card Title")
                .font(.headline)
            Text("Description with dynamic type support")
                .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, maksimum erişilebilirlik boyutunda 16 pt'den ~27 pt'ye ölçeklenir. spacing — 8'den ~14 pt'ye. Bu, kartın herhangi bir yazı tipi boyutunda görsel olarak dengeli kalmasını sağlar.

@ScaledMetric örnekleri

Örnek: Dynamic Type destekli simge. Image(systemName:) simge boyutları varsayılan olarak Dynamic Type'a göre ölçeklenmez. @ScaledMetric, geçerli ölçek faktörüne dayalı olarak imageScale veya çerçeve boyutunu değiştirerek bu sorunu çözer.

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

Örnek: Erişilebilir rozet bileşeni. Numaralı bir rozet, metinle orantılı olarak ölçeklenmelidir. Minimum rozet boyutu için @ScaledMetric, yuvarlak rozetin büyük metinle görünür kalmasını sağlar.

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, büyütülmüş badgeSize ile eşleşmek için Circle içeriğini ek olarak ölçeklendirir. fontScale olmadan, rozetin içindeki metin büyük metinle sığmayabilir.

@ScaledMetric vs @State — farklar

@ScaledMetric ve @State, her ikisi de değişiklikleri izleyen property wrapper'lardır, ancak farklı güncelleme kaynaklarına sahiptir. @State, programatik değişiklikte ($stateBinding aracılığıyla) değeri günceller. @ScaledMetric, sistem Dynamic Type'ı değiştiğinde değeri otomatik olarak günceller ancak değerin doğrudan koddan değiştirilmesine izin vermez.

Temel fark: @ScaledMetric geliştirici için salt okunur ve sistem için salt yazılırdır. Bir setter aracılığıyla scaledValue'ı değiştiremezsiniz — SwiftUI tarafından temel değer ve geçerli Dynamic Type temel alınarak hesaplanır. @State ise tamamen geliştirici tarafından kontrol edilir. Dynamic Type'a göre ölçeklenen ve aynı zamanda programatik olarak değişen bir değere ihtiyacınız varsa — @ScaledMetric'i @State ile birleştirin veya hesaplanmış bir özellik kullanın.

Özellik@ScaledMetric@State
Güncelleme kaynağıDynamic Type (sistem)Programatik (geliştirici)
Değer türüCGFloat, Int, DoubleHerhangi biri
Koddan değişiklik İzin verilmezBinding ile izin verilir
View yeniden çizimiDynamic Type değişimindeDeğer değişiminde
iOS sürümüiOS 14+iOS 13+

Birleşik desen: Dynamic Type'a göre ölçeklendirirken programatik olarak padding'i değiştirmeniz gerekiyorsa (örneğin, dokunma animasyonu), temel ölçeklenmiş değer için @ScaledMetric ve animasyon çarpanı için @State oluşturun. Nihai değer = scaledValue × animationMultiplier.

@ScaledMetric ile yaygın hatalar

Hata 1: Yazı tipleri için @ScaledMetric kullanmak. @ScaledMetric sayıları ölçeklendirir, yazı tiplerini değil. Yazı tipleri için .font(.body) kullanın — SwiftUI Dynamic Type'ı otomatik olarak uygular. @ScaledMetric'i font(.system(size: scaledSize)) ile asla kullanmayın — sistem erişilebilirliğini bozar.

Hata 2: Heterojen öğeler için relativeTo eksikliği. Padding'iniz (.body'ye bağlı) ve iconSize'ınız (.title'a bağlı) varsa, her biri için doğru relativeTo'yu belirtin. relativeTo olmadan, her ikisi de .body tarafından ölçeklenir ve simgenin metin bağlamına göre orantısız ölçeklenmesine neden olur.

Hata 3: ViewModel/@ObservableObject içinde @ScaledMetric. @ScaledMetric, yalnızca bir View içinde çalışan bir SwiftUI property wrapper'ıdır. ViewModel'lerde veya hizmetlerde kullanılamaz. ViewModel'de ölçekleme için, ölçeklenmiş değeri View'den parametre olarak iletin veya View'de @Environment(\.sizeCategory) kullanın.

Kodda geçerli boyut kategorisini alma

@Environment(\.sizeCategory), bir View'de geçerli Dynamic Type'ı almanın alternatif bir yoludur. Daha fazla kontrole ihtiyacınız olduğunda kullanın: özel bir çarpan hesaplama, sizeCategory'yi ViewModel'e iletme veya esnek ölçekleme için @ScaledMetric ile birleştirme.

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("Custom scaled content")
            .font(.body)
            .padding(basePadding + extraPadding)
    }
}

Ekstra padding extraPadding yalnızca erişilebilirlik boyutlarında eklenir ve temel @ScaledMetric mantığını değiştirmeden büyük metin için daha fazla alan sağlar.

Sıkça sorulan sorular

@ScaledMetric, @ScaledFont'dan nasıl farklıdır?

@ScaledMetric, sayıları ölçeklendirmek için resmi bir SwiftUI property wrapper'ıdır. @ScaledFont standart bir API olarak mevcut değildir — topluluk tarafından uygulanan özel bir wrapper'dır. Yazı tipleri için her zaman metin stilleriyle (.body, .title) yerleşik .font() kullanın ve padding, spacing ve boyutlar için @ScaledMetric kullanın.

@ScaledMetric watchOS ve tvOS'ta çalışır mı?

@ScaledMetric, iOS 14+, watchOS 7+, tvOS 14+ ve macOS 11+'da kullanılabilir. watchOS'ta Dynamic Type daha küçük bir aralıkla sınırlıdır — .extraSmall'dan .extraLarge'ye kadar boyutlar erişilebilirlik boyutları olmadan kullanılabilir. tvOS'ta Dynamic Type mevcut değildir — @ScaledMetric her zaman temel değeri döndürür.

@ScaledMetric birim testlerinde test edilebilir mi?

Evet, @ScaledMetric'i test etmek için @ScaledMetric ile bir View oluşturun ve .environment(\.sizeCategory, .extraExtraLarge) aracılığıyla ortam .sizeCategory değerini iletin. Ardından GeometryReader veya SwiftUI Inspector aracılığıyla öğe boyutunu alın. Alternatif olarak, ayrı bir modülte UIFontMetrics aracılığıyla ölçekleme mantığını test edin.

@ScaledMetric, .dynamicTypeSize ile nasıl etkileşime girer?

.dynamicTypeSize, bir hiyerarşi için maksimum Dynamic Type'ı sınırlayan bir View değiştiricisidir (örneğin, .dynamicTypeSize(...large)). @ScaledMetric bu sınırlamaya saygı duyar: .dynamicTypeSize ayarlanmışsa, ölçeklenmiş değer karşılık gelen boyutu aşmaz. Hassas kontrol için her iki API'yi birleştirin.

@ScaledMetric arayüzü güncellemezse ne yapmalı?

View'in @ScaledMetric'i dahili olarak kullandığından emin olun (bir ViewModel'de değil). View'in Dynamic Type'a abone olduğunu kontrol edin: @ScaledMetric otomatik olarak body yenilemesini tetikler, ancak View .equatable() veya .id() kullanırsa mekanizma bozulabilir. Yedek olarak @Environment(\.sizeCategory) kullanın.

Özet

  • @ScaledMetric — Dynamic Type'a otomatik sayı ölçekleme için bir SwiftUI property wrapper'ı.
  • Bağlama — relativeTo, ölçeği belirli bir metin stiline (body, title, caption) bağlar.
  • Erişilebilirlik — @ScaledMetric, manuel kod olmadan arayüz erişilebilirliğini artırır.
  • Yalnızca sayılar — wrapper, CGFloat, Int, Double'ı ölçeklendirir ancak yazı tiplerini ölçeklendirmez.
  • Aralık — temel değere göre 0.85 (XS) ile 1.71 (XXXL) arası.
  • Salt okunur — @ScaledMetric koddan değiştirilemez, yalnızca sistem aracılığıyla.

Anahtar teslim bir mobil uygulama geliştireceğiz

IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.

Projeyi tartış

Ayrıca okuyun