@ScaledMetric은 사용자의 Dynamic Type 설정에 따라 숫자 값을 자동으로 조정하는 SwiftUI property wrapper입니다. 값은 @ScaledMetric에 래핑되며 시스템 폰트 크기가 변경될 때 다시 계산되어 시각 장애가 있는 사람들을 위한 인터페이스 접근성을 보장합니다. Apple Developer Documentation(2026)에 따르면 @ScaledMetric은 UIFontMetrics 척도를 사용하여 preferred content size category를 기반으로 상대적 척도를 계산합니다. 접근성에 대한 자세한 내용은 SwiftUI 접근성 문서를 참조하세요.
핵심 요점
@ScaledMetric은 iOS 14에 추가된 SwiftUI property wrapper로, 숫자 값(CGFloat, Int, Double)을 현재 Dynamic Type 폰트 크기에 자동으로 조정합니다. 폰트용 .font(.body)와 달리 @ScaledMetric은 padding, spacing, cornerRadius, iconSize 등 큰 텍스트에 비례하여 증가해야 하는 모든 숫자 매개변수를 스케일링합니다.
@ScaledMetric의 주요 목적은 비텍스트 인터페이스 요소에 접근성 스케일링을 제공하는 것입니다. 사용자가 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("Submit", systemImage: "checkmark.circle.fill")
.font(.body)
.padding(padding)
.imageScale(.init(rawValue: iconSize / 24) ?? .medium)
}
}
padding은 .body(기본값)를 기준으로 스케일링되고, iconSize는 .title을 기준으로 스케일링됩니다. 큰 텍스트에서는 간격과 아이콘이 비례적으로 증가합니다. @ScaledMetric이 없으면 padding은 폰트 크기와 관계없이 12pt로 유지되어 시각적 불균형이 발생합니다.
@ScaledMetric 메커니즘은 UIKit의 UIFontMetrics를 기반으로 합니다. SwiftUI가 @ScaledMetric 인스턴스를 생성하면 현재 preferred content size category(UIContentSizeCategory)를 기반으로 승수를 계산합니다. 기본값은 지정된 텍스트 스타일에 대한 UIFontMetrics의 scaledValue로 곱해집니다.
수학적으로: ScaledMetricValue = baseValue × UIFontMetrics.scaledValue(for: relativeTo). relativeTo가 지정되지 않으면 .body에 바인딩된 UIFontMetrics.default가 사용됩니다. Dynamic Type이 변경되면 SwiftUI가 View 본문을 다시 만들고, @ScaledMetric이 새 scaledValue를 계산하며, UI는 @State와 유사한 PropertyWrappers 메커니즘을 통해 자동으로 업데이트됩니다.
iOS 척도는 11가지 크기를 포함합니다: .extraSmall(5pt)부터 .accessibilityExtraExtraExtraLarge(.body의 경우 77pt)까지입니다. .body의 스케일링 계수는 기본값을 기준으로 0.85(XS)에서 1.71(XXXL)까지입니다. @ScaledMetric은 정확히 이 척도를 사용하므로 12pt padding 값은 최대 접근성 크기에서 약 20pt가 될 수 있습니다.
| 콘텐츠 크기 카테고리 | 계수 (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 선택: 본문 텍스트와 관련된 값(목록의 padding, spacing)에는 .body를, 큰 요소(iconSize, imageSize)에는 .title을, 작은 요소(배지 크기)에는 .caption을 사용하세요. 이렇게 하면 요소가 주변 텍스트와 동기화되어 스케일링됩니다.
Dynamic Type은 사용자가 설정 → 디스플레이 및 밝기 → 텍스트 크기에서 시스템 폰트 크기를 조정할 수 있는 iOS 기능입니다. 변경 사항은 모든 앱에 전역적으로 적용됩니다. @ScaledMetric은 이 변경에 자동으로 반응합니다: UIContentSizeCategory가 변경되면 SwiftUI가 모든 @ScaledMetric 변수를 업데이트합니다.
중요: @ScaledMetric은 숫자 값만 스케일링하며 폰트를 직접 관리하지 않습니다. 폰트의 경우 텍스트 스타일(.body, .title, .headline)과 함께 .font()를 사용하세요 — SwiftUI가 자동으로 폰트를 스케일링합니다. @ScaledMetric은 padding, spacing 및 요소 크기에 대한 폰트 스케일링을 보완합니다.
Canvas Preview는 Dynamic Type을 지원합니다: Canvas 도구 모음에는 다양한 폰트 크기에서 UI를 테스트할 수 있는 텍스트 크기 슬라이더(A–A)가 있습니다. @ScaledMetric과 함께 사용하여 간격과 크기가 올바르게 스케일링되는지 확인하세요.
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는 최대 접근성 크기에서 16pt에서 약 27pt로 스케일링됩니다. spacing은 8에서 약 14pt로 스케일링됩니다. 이렇게 하면 카드가 어떤 폰트 크기에서도 시각적으로 균형을 유지합니다.
예제: Dynamic Type 지원 아이콘. Image(systemName:) 아이콘 크기는 기본적으로 Dynamic Type에 맞게 조정되지 않습니다. @ScaledMetric은 현재 스케일 계수에 따라 imageScale 또는 프레임 크기를 변경하여 이 문제를 해결합니다.
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)
}
}
}
예제: 접근 가능한 배지 컴포넌트. 번호가 매겨진 배지는 텍스트에 비례하여 스케일링되어야 합니다. 최소 배지 크기에 @ScaledMetric을 사용하면 큰 텍스트에서도 원형 배지가 계속 표시됩니다.
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은 확대된 badgeSize에 맞게 Circle 콘텐츠를 추가로 스케일링합니다. fontScale이 없으면 큰 텍스트에서 배지 내부의 텍스트가 맞지 않을 수 있습니다.
@ScaledMetric과 @State는 모두 변경 사항을 추적하는 property wrapper이지만 업데이트 소스가 다릅니다. @State는 프로그래매틱 변경($stateBinding을 통해) 시 값을 업데이트합니다. @ScaledMetric은 시스템 Dynamic Type이 변경될 때 자동으로 값을 업데이트하지만 코드에서 직접 값을 변경할 수는 없습니다.
핵심 차이점: @ScaledMetric은 개발자에게 읽기 전용이고 시스템에게 쓰기 전용입니다. setter를 통해 scaledValue를 변경할 수 없습니다 — SwiftUI가 기본값과 현재 Dynamic Type을 기반으로 계산합니다. 반면 @State는 개발자가 완전히 제어합니다. Dynamic Type에 따라 스케일링되면서 프로그래매틱하게도 변경되는 값이 필요하다면 @ScaledMetric을 @State와 결합하거나 계산된 속성을 사용하세요.
| 특성 | @ScaledMetric | @State |
|---|---|---|
| 업데이트 소스 | Dynamic Type (시스템) | 프로그래매틱 (개발자) |
| 값 유형 | CGFloat, Int, Double | 모든 유형 |
| 코드 변경 | 불가능 | binding을 통해 가능 |
| View 다시 그리기 | Dynamic Type 변경 시 | 값 변경 시 |
| iOS 버전 | iOS 14+ | iOS 13+ |
결합 패턴: Dynamic Type에 맞게 스케일링하면서 프로그래매틱하게 padding을 변경해야 하는 경우(예: 탭 애니메이션), 기본 스케일 값에 @ScaledMetric을 만들고 애니메이션 승수에 @State를 만듭니다. 최종 값 = scaledValue × animationMultiplier.
실수 1: 폰트에 @ScaledMetric 사용. @ScaledMetric은 숫자를 스케일링하고 폰트는 스케일링하지 않습니다. 폰트에는 .font(.body)를 사용하세요 — SwiftUI가 자동으로 Dynamic Type을 적용합니다. @ScaledMetric을 font(.system(size: scaledSize))와 함께 절대 사용하지 마세요 — 시스템 접근성이 손상됩니다.
실수 2: 이기종 요소에 relativeTo 누락. padding(.body에 바인딩)과 iconSize(.title에 바인딩)가 있는 경우 각각에 올바른 relativeTo를 지정하세요. relativeTo가 없으면 둘 다 .body로 스케일링되어 텍스트 컨텍스트에 비해 아이콘이 불균형하게 스케일링됩니다.
실수 3: ViewModel/@ObservableObject에서 @ScaledMetric 사용. @ScaledMetric은 View 내에서만 작동하는 SwiftUI property wrapper입니다. ViewModel이나 서비스에서는 사용할 수 없습니다. ViewModel에서 스케일링하려면 View에서 스케일된 값을 매개변수로 전달하거나 View에서 @Environment(\.sizeCategory)를 사용하세요.
@Environment(\.sizeCategory)는 View에서 현재 Dynamic Type을 가져오는 대체 방법입니다. 사용자 정의 승수 계산, ViewModel에 sizeCategory 전달, @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("Custom scaled content")
.font(.body)
.padding(basePadding + extraPadding)
}
}
추가 padding extraPadding은 접근성 크기에서만 추가되어 기본 @ScaledMetric 로직을 변경하지 않고 큰 텍스트에 더 많은 공간을 제공합니다.
자주 묻는 질문
@ScaledMetric은 숫자 스케일링을 위한 공식 SwiftUI property wrapper입니다. @ScaledFont는 표준 API로 존재하지 않습니다 — 커뮤니티에서 구현한 사용자 정의 wrapper입니다. 폰트의 경우 항상 텍스트 스타일(.body, .title)과 함께 내장 .font()를 사용하고 padding, spacing 및 크기에는 @ScaledMetric을 사용하세요.
@ScaledMetric은 iOS 14+, watchOS 7+, tvOS 14+ 및 macOS 11+에서 사용할 수 있습니다. watchOS에서 Dynamic Type은 더 작은 범위로 제한됩니다 — 접근성 크기 없이 .extraSmall에서 .extraLarge까지의 크기를 사용할 수 있습니다. tvOS에서는 Dynamic Type을 사용할 수 없습니다 — @ScaledMetric은 항상 기본값을 반환합니다.
네, @ScaledMetric을 테스트하려면 @ScaledMetric이 있는 View를 만들고 .environment(\.sizeCategory, .extraExtraLarge)를 통해 환경 .sizeCategory 값을 전달하세요. 그런 다음 GeometryReader 또는 SwiftUI Inspector를 통해 요소 크기를 가져옵니다. 또는 별도 모듈에서 UIFontMetrics를 통해 스케일링 로직을 테스트하세요.
.dynamicTypeSize는 계층 구조의 최대 Dynamic Type을 제한하는 View 수정자입니다(예: .dynamicTypeSize(...large)). @ScaledMetric은 이 제한을 따릅니다: .dynamicTypeSize가 설정되면 스케일된 값이 해당 크기를 초과하지 않습니다. 정밀한 제어를 위해 두 API를 결합하세요.
View가 내부적으로 @ScaledMetric을 사용하는지 확인하세요(ViewModel이 아님). View가 Dynamic Type을 구독하는지 확인하세요: @ScaledMetric은 자동으로 body 새로고침을 트리거하지만 View가 .equatable() 또는 .id()를 사용하는 경우 메커니즘이 손상될 수 있습니다. 폴백으로 @Environment(\.sizeCategory)를 사용하세요.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.