@ScaledMetric — property wrapper SwiftUI који аутоматски скалира нумеричку вредност у складу са подешавањима Dynamic Type корисника. Вредност се обавија у @ScaledMetric и прерачунава при промени системске величине фонта, што гарантује приступачност интерфејса за особе са оштећењем вида. Према Apple Developer Documentation (2026), @ScaledMetric користи скалу UIFontMetrics за израчунавање релативне скале на основу preferred content size category. Више о accessibility прочитајте у материјалу о accessibility SwiftUI.
Главно
@ScaledMetric — property wrapper SwiftUI, додат у iOS 14, који аутоматски скалира нумеричку вредност (CGFloat, Int, Double) под тренутну величину фонта Dynamic Type. За разлику од .font(.body) за фонтове, @ScaledMetric скалира било које нумеричке параметре: padding, spacing, cornerRadius, iconSize — све што треба пропорционално да се повећа при великом тексту.
Основни задатак @ScaledMetric је да обезбеди accessibility-скалирање не-текстуалних елемената интерфејса. Када корисник повећа фонт у подешавањима iOS-а, дугмићи, иконе и размаци треба да се повећају пропорционално како би интерфејс остао уравнотежен. @ScaledMetric решава овај задатак аутоматски, без ручног израчунавања множилаца.
Основна синтакса @ScaledMetric користи подразумевану вредност и опциони параметар relativeTo. Ако је relativeTo наведен, скалирање је везано за конкретан текстуални стил (UIFontTextStyle). Ако није — користи се скала .body.
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 се заснива на 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 се ажурира аутоматски кроз механизам @State-сличних PropertyWrappers.
iOS скала укључује 11 величина: од .extraSmall (5 pt) до .accessibilityExtraExtraExtraLarge (77 pt за .body). Коефицијент скалирања за .body варира од 0.85 (XS) до 1.71 (XXXL) у односу на основну вредност. @ScaledMetric користи управо ову скалу, тако да вредност 12 pt за padding може постати ~20 pt при максималној accessibility величини.
| Content Size Category | Коефицијент (body) | Пример @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 |
Избор relativeTo: користите .body за вредности везане за главни текст (padding, spacing у листама), .title за велике елементе (iconSize, imageSize), .caption за мале елементе (величина badge-а). Ово гарантује да се елементи скалирају у складу са околним текстом.
Dynamic Type — iOS функција која омогућава кориснику да подеси величину системског фонта у Settings → Display & Brightness → Text Size. Промена се примењује глобално на све апликације. @ScaledMetric реагује на ову промену аутоматски: SwiftUI ажурира све @ScaledMetric променљиве при промени UIContentSizeCategory.
Важно: @ScaledMetric скалира само нумеричке вредности, али не управља директно фонтовима. За фонтове користите .font() са текстуалним стилом (.body, .title, .headline) — SwiftUI аутоматски скалира фонт. @ScaledMetric допуњује скалирање фонта за padding, spacing и величине елемената.
Canvas Preview подржава Dynamic Type: на панелу алата Canvas налази се клизач Text Size (A–A) за проверу UI при различитим величинама фонта. Користите га са @ScaledMetric да бисте се уверили да се размаци и величине правилно скалирају.
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 при максималној accessibility величини. spacing — од 8 до ~14 pt. Ово гарантује да картица остаје визуелно уравнотежена при било којој величини фонта.
Пример: икона са подршком за Dynamic Type. Величина икона Image(systemName:) подразумевано не скалира под Dynamic Type. @ScaledMetric решава овај проблем: мењајући imageScale или frame величину на основу тренутног фактора скале.
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 остаје видљив при великом фонту.
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 и @State — оба property wrapper-а који прате промене, али са различитим изворима ажурирања. @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 | При промени вредности |
| iOS верзија | iOS 14+ | iOS 13+ |
Комбиновани образац: ако треба да мењате padding програмски (нпр. анимација притиска) и истовремено да скалирате под Dynamic Type, креирајте @ScaledMetric за основну скалирану вредност и @State за множилац анимације. Крајња вредност = scaledValue × animationMultiplier.
Грешка 1: коришћење @ScaledMetric за фонтове. @ScaledMetric скалира бројеве, а не фонтове. За фонтове користите .font(.body) — SwiftUI аутоматски примењује Dynamic Type. Никада не користите @ScaledMetric са font(.system(size: scaledSize)) — то квари системско accessibility.
Грешка 2: недостатак relativeTo за различите елементе. Ако имате padding (везан за .body) и iconSize (везан за .title), наведите тачан relativeTo за сваки. Без relativeTo, оба ће се скалирати по .body, што ће довести до непропорционалног повећања иконе у односу на њен текстуални контекст.
Грешка 3: @ScaledMetric у ViewModel/@ObservableObject. @ScaledMetric је SwiftUI property wrapper који ради само унутар View-а. Не може се користити у ViewModel-у или сервисима. За скалирање у ViewModel-у, проследите скалирану вредност из View-а као параметар или користите @Environment(\.sizeCategory) у View-у.
@Environment(\.sizeCategory) — алтернативни начин добијања тренутног Dynamic Type у View-у. Користите га када вам треба више контроле: израчунавање прилагођеног множиоца, прослеђивање sizeCategory у ViewModel или комбиновање са @ScaledMetric за флексибилно скалирање.
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 се додаје само при accessibility величинама, дајући више простора за велики текст без промене основне @ScaledMetric логике.
Често постављана питања
@ScaledMetric — званични property wrapper SwiftUI за скалирање бројева. @ScaledFont не постоји као стандардни API — то је прилагођени wrapper који је имплементирала заједница. За фонтове увек користите уграђени .font() са текстуалним стиловима (.body, .title), а @ScaledMetric — за padding, spacing и величине.
@ScaledMetric је доступан на iOS 14+, watchOS 7+, tvOS 14+ и macOS 11+. На watchOS је Dynamic Type ограничен на мањи опсег — доступне су величине од .extraSmall до .extraLarge без accessibility величина. На tvOS Dynamic Type не постоји — @ScaledMetric увек враћа основну вредност.
Да, за тестирање @ScaledMetric креирајте View са @ScaledMetric и проследите environment вредност .sizeCategory кроз .environment(\.sizeCategory, .extraExtraLarge). Затим добијте величину елемента кроз GeometryReader или SwiftUI Inspector. Алтернативно, проверавајте логику скалирања кроз UIFontMetrics у посебном модулу.
.dynamicTypeSize — View модификатор који ограничава максимални Dynamic Type за хијерархију (нпр. .dynamicTypeSize(...large)). @ScaledMetric узима у обзир ово ограничење: ако је .dynamicTypeSize постављен, скалирана вредност неће премашити одговарајућу величину. Комбинујте оба API-ја за прецизну контролу.
Уверите се да View користи @ScaledMetric унутар себе (не у ViewModel-у). Проверите да View претплаћује на Dynamic Type: @ScaledMetric аутоматски позива освежавање body-ја, али ако View користи .equatable() или .id(), механизам може да се поквари. Користите @Environment(\.sizeCategory) као fallback.
Резиме
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође