@ScaledMetric — property wrapper SwiftUI yang secara otomatis menskalakan nilai numerik sesuai dengan pengaturan Dynamic Type pengguna. Nilai dibungkus dalam @ScaledMetric dan dihitung ulang saat ukuran font sistem berubah, menjamin aksesibilitas antarmuka bagi orang dengan gangguan penglihatan. Menurut Apple Developer Documentation (2026), @ScaledMetric menggunakan skala UIFontMetrics untuk menghitung skala relatif berdasarkan preferred content size category. Baca lebih lanjut tentang aksesibilitas di materi tentang aksesibilitas SwiftUI.
Poin utama
@ScaledMetric — property wrapper SwiftUI, ditambahkan di iOS 14, yang secara otomatis menskalakan nilai numerik (CGFloat, Int, Double) ke ukuran font Dynamic Type saat ini. Tidak seperti .font(.body) untuk font, @ScaledMetric menskalakan parameter numerik apa pun: padding, spacing, cornerRadius, iconSize — semua yang harus meningkat secara proporsional saat teks besar.
Tugas utama @ScaledMetric adalah menyediakan penskalaan aksesibilitas elemen antarmuka non-teks. Saat pengguna memperbesar font di pengaturan iOS, tombol, ikon, dan spasi harus meningkat secara proporsional agar antarmuka tetap seimbang. @ScaledMetric menyelesaikan tugas ini secara otomatis, tanpa perhitungan manual pengali.
Sintaks dasar @ScaledMetric menggunakan nilai default dan parameter opsional relativeTo. Jika relativeTo ditentukan, penskalaan terikat pada gaya teks tertentu (UIFontTextStyle). Jika tidak — skala .body digunakan.
struct AccessibleButton: View {
@ScaledMetric private var padding: CGFloat = 12
@ScaledMetric(relativeTo: .title) private var iconSize: CGFloat = 24
var body: some View {
Label("Kirim", systemImage: "checkmark.circle.fill")
.font(.body)
.padding(padding)
.imageScale(.init(rawValue: iconSize / 24) ?? .medium)
}
}
padding akan diskalakan relatif terhadap .body (default), iconSize — relatif terhadap .title. Pada teks besar, spasi dan ikon akan meningkat secara proporsional. Tanpa @ScaledMetric, spasi akan tetap 12 pt pada ukuran font apa pun, yang menyebabkan ketidakseimbangan visual.
Mekanisme @ScaledMetric didasarkan pada UIFontMetrics dari UIKit. Saat SwiftUI membuat instance @ScaledMetric, ia menghitung pengali berdasarkan preferred content size category saat ini (UIContentSizeCategory). Nilai dasar dikalikan dengan scaledValue dari UIFontMetrics untuk gaya teks yang ditentukan.
Secara matematis: ScaledMetricValue = baseValue × UIFontMetrics.scaledValue(for: relativeTo). Jika relativeTo tidak ditentukan, UIFontMetrics.default yang terikat pada .body digunakan. Saat Dynamic Type berubah, SwiftUI membuat ulang body View, @ScaledMetric menghitung scaledValue baru dan UI diperbarui secara otomatis melalui mekanisme PropertyWrappers mirip @State.
Skala iOS mencakup 11 ukuran: dari .extraSmall (5 pt) hingga .accessibilityExtraExtraExtraLarge (77 pt untuk .body). Koefisien penskalaan untuk .body bervariasi dari 0.85 (XS) hingga 1.71 (XXXL) relatif terhadap nilai dasar. @ScaledMetric menggunakan skala ini, jadi nilai 12 pt untuk padding dapat menjadi ~20 pt pada ukuran aksesibilitas maksimum.
| Content Size Category | Koefisien (body) | Contoh @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 |
Pemilihan relativeTo: gunakan .body untuk nilai yang terkait dengan teks utama (padding, spacing dalam daftar), .title untuk elemen besar (iconSize, imageSize), .caption untuk elemen kecil (ukuran badge). Ini memastikan elemen menskala seiring dengan teks di sekitarnya.
Dynamic Type — fitur iOS yang memungkinkan pengguna menyesuaikan ukuran font sistem di Settings → Display & Brightness → Text Size. Perubahan diterapkan secara global ke semua aplikasi. @ScaledMetric bereaksi terhadap perubahan ini secara otomatis: SwiftUI memperbarui semua variabel @ScaledMetric saat UIContentSizeCategory berubah.
Penting: @ScaledMetric hanya menskalakan nilai numerik, tetapi tidak mengelola font secara langsung. Untuk font gunakan .font() dengan gaya teks (.body, .title, .headline) — SwiftUI secara otomatis menskalakan font. @ScaledMetric melengkapi penskalaan font untuk padding, spacing, dan ukuran elemen.
Canvas Preview mendukung Dynamic Type: di bilah alat Canvas ada penggeser Text Size (A–A) untuk memeriksa UI pada berbagai ukuran font. Gunakan dengan @ScaledMetric untuk memastikan spasi dan ukuran menskala dengan benar.
struct CardView: View {
@ScaledMetric private var cornerRadius: CGFloat = 16
@ScaledMetric private var spacing: CGFloat = 8
var body: some View {
VStack(spacing: spacing) {
Text("Judul kartu")
.font(.headline)
Text("Deskripsi dengan dukungan 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 menskala dari 16 pt hingga ~27 pt pada ukuran aksesibilitas maksimum. spacing — dari 8 hingga ~14 pt. Ini memastikan kartu tetap seimbang secara visual pada ukuran font apa pun.
Contoh: ikon dengan dukungan Dynamic Type. Ukuran ikon Image(systemName:) secara default tidak diskalakan di bawah Dynamic Type. @ScaledMetric memecahkan masalah ini: dengan mengubah imageScale atau ukuran frame berdasarkan faktor skala saat ini.
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)
}
}
}
Contoh: komponen badge yang dapat diakses. Badge dengan angka harus menskala secara proporsional dengan teks. @ScaledMetric untuk ukuran minimum badge memastikan badge bundar tetap terlihat pada font besar.
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 juga menskalakan konten Circle agar sesuai dengan badgeSize yang diperbesar. Tanpa fontScale, teks di dalam badge mungkin tidak muat pada font besar.
@ScaledMetric dan @State — keduanya property wrapper yang melacak perubahan, tetapi dengan sumber pembaruan berbeda. @State memperbarui nilai saat perubahan pemrograman (melalui $stateBinding). @ScaledMetric memperbarui nilai secara otomatis saat Dynamic Type sistem berubah, tetapi tidak mengizinkan perubahan nilai langsung dari kode.
Perbedaan utama: @ScaledMetric — read-only untuk pengembang dan write-only untuk sistem. Anda tidak dapat mengubah scaledValue melalui setter — dihitung oleh SwiftUI berdasarkan nilai dasar dan Dynamic Type saat ini. @State, sebaliknya, sepenuhnya dikelola oleh pengembang. Jika Anda memerlukan nilai yang baik menskala di bawah Dynamic Type maupun berubah secara terprogram — gabungkan @ScaledMetric dengan @State atau gunakan properti terhitung.
| Karakteristik | @ScaledMetric | @State |
|---|---|---|
| Sumber pembaruan | Dynamic Type (sistem) | Terprogram (pengembang) |
| Tipe nilai | CGFloat, Int, Double | Semua tipe |
| Perubahan dari kode | Tidak bisa | Bisa melalui binding |
| Gambar ulang View | Saat Dynamic Type berubah | Saat nilai berubah |
| Versi iOS | iOS 14+ | iOS 13+ |
Pola gabungan: jika Anda perlu mengubah padding secara terprogram (misalnya, animasi tekan) dan sekaligus menskalakan di bawah Dynamic Type, buat @ScaledMetric untuk nilai skala dasar dan @State untuk pengali animasi. Nilai akhir = scaledValue × animationMultiplier.
Kesalahan 1: menggunakan @ScaledMetric untuk font. @ScaledMetric menskalakan angka, bukan font. Untuk font gunakan .font(.body) — SwiftUI secara otomatis menerapkan Dynamic Type. Jangan pernah menggunakan @ScaledMetric dengan font(.system(size: scaledSize)) — ini merusak aksesibilitas sistem.
Kesalahan 2: tidak ada relativeTo untuk elemen heterogen. Jika Anda memiliki padding (terkait dengan .body) dan iconSize (terkait dengan .title), tentukan relativeTo yang benar untuk masing-masing. Tanpa relativeTo, keduanya akan diskalakan menurut .body, yang menyebabkan peningkatan ikon yang tidak proporsional relatif terhadap konteks teksnya.
Kesalahan 3: @ScaledMetric di ViewModel/@ObservableObject. @ScaledMetric adalah property wrapper SwiftUI yang hanya berfungsi di dalam View. Tidak dapat digunakan di ViewModel atau layanan. Untuk penskalaan di ViewModel, kirim nilai skala dari View sebagai parameter atau gunakan @Environment(\.sizeCategory) di View.
@Environment(\.sizeCategory) — cara alternatif untuk mendapatkan Dynamic Type saat ini di View. Gunakan saat Anda membutuhkan lebih banyak kontrol: menghitung pengali kustom, mengirimkan sizeCategory ke ViewModel, atau menggabungkan dengan @ScaledMetric untuk penskalaan fleksibel.
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("Konten skala kustom")
.font(.body)
.padding(basePadding + extraPadding)
}
}
Spasi tambahan extraPadding ditambahkan hanya pada ukuran aksesibilitas, memberikan lebih banyak ruang untuk teks besar tanpa mengubah logika dasar @ScaledMetric.
Pertanyaan yang sering diajukan
@ScaledMetric — property wrapper resmi SwiftUI untuk menskalakan angka. @ScaledFont tidak ada sebagai API standar — ini adalah wrapper kustom yang diimplementasikan oleh komunitas. Untuk font selalu gunakan .font() bawaan dengan gaya teks (.body, .title), dan @ScaledMetric — untuk padding, spacing, dan ukuran.
@ScaledMetric tersedia di iOS 14+, watchOS 7+, tvOS 14+ dan macOS 11+. Di watchOS, Dynamic Type terbatas pada rentang yang lebih kecil — ukuran dari .extraSmall hingga .extraLarge tanpa ukuran aksesibilitas. Di tvOS, Dynamic Type tidak ada — @ScaledMetric selalu mengembalikan nilai dasar.
Ya, untuk menguji @ScaledMetric buat View dengan @ScaledMetric dan kirim nilai environment .sizeCategory melalui .environment(\.sizeCategory, .extraExtraLarge). Kemudian dapatkan ukuran elemen melalui GeometryReader atau SwiftUI Inspector. Alternatifnya, periksa logika penskalaan melalui UIFontMetrics di modul terpisah.
.dynamicTypeSize — modifier View yang membatasi Dynamic Type maksimum untuk hierarki (misalnya, .dynamicTypeSize(...large)). @ScaledMetric mempertimbangkan batasan ini: jika .dynamicTypeSize diatur, nilai skala tidak akan melebihi ukuran yang sesuai. Gabungkan kedua API untuk kontrol yang presisi.
Pastikan View menggunakan @ScaledMetric di dalamnya (bukan di ViewModel). Periksa bahwa View berlangganan Dynamic Type: @ScaledMetric secara otomatis memicu penyegaran body, tetapi jika View menggunakan .equatable() atau .id(), mekanisme bisa rusak. Gunakan @Environment(\.sizeCategory) sebagai fallback.
Ringkasan
Kami akan mengembangkan aplikasi seluler turnkey
IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.
Baca juga