SwiftUI - ScaledMetric
안녕하세요. 그린입니다 🍏
이번 포스팅에서는 이미지 에셋의 크기에 대해 ScaledMetric API를 적용해 다이나믹 타입에 대처하는 방법에 대해 알아보겠습니다 🙋🏻
올해 WWDC 2024에서 Get started with Dynamic Type이라는 세션이 공개되었습니다.
해당 세션에서 다이나믹 타입에 대해 전반적으로 소개하며 다루고 있는데요.
여기서 ScaledMetric이라는 프로퍼티 래퍼로 우리의 이미지 에셋 크기를 다이나믹 타입을 적용할 수 있다고 소개하고 있어요 😃
그래서 그 ScaledMetric API가 어떤건지 핵심만 정리해봅니다!
Get started with Dynamic Type 세션을 정리한 포스팅도 있으니 많은 관심 부탁드립니다 🙇🏻
ScaledMetric
해당 API는 iOS 14.0 이상부터는 사용할 수 있었던 원래 있던 프로퍼티 래퍼입니다.
(한번도 쓴적이 없어서 몰랐을뿐 🥲)
@propertyWrapper
struct ScaledMetric<Value> where Value : BinaryFloatingPoint
BinaryFloatingPoint 타입인 value 즉, 숫자 값을 조정하는 다이나믹 타입이라고 볼 수 있어요.
다이나믹 타입이 적용될 수 있는 값인것이죠.
또한, 해당 API는 DynamicProperty와 Sendable 프로토콜을 준수하고 있습니다 🙋🏻
SwiftUI에서 제공하며 해당 프로퍼티 래퍼는 사용자의 선호하는 폰트 크기에 따라 자동으로 조정되는 수치 값을 생성해줍니다.
이를 통해서 우리는 앱의 UI 요소들이 사용자의 접근성 설정에 맞춰 일관되게 크기가 조정되는것이죠.
즉, 폰트 크기를 시스템에서 크게 가져갔는데 폰트만 커지고 이미지는 여전히 작은 경험에서 벗어나 같이 확장되는 그런 접근성을 구현해낼 수 있죠 😃
물론, 이미지 에셋의 크기뿐 아니라 패딩이나 다양한 수치에서도 활용할 수 있어요.
그럼 이제 실제 예시 코드로 볼께요 👯♂️
생성하고 사용하는 방법에는 두가지가 있습니다.
첫번째로, 특정 텍스트 스타일에 대해 상대적으로 크기를 조정하는 방법이 아닌 기본적으로 사용하는 방법부터 보겠습니다.
init(wrappedValue: Value)
초기화는 아주 간단합니다.
단순 값만 부여해주면 되는것이죠.
코드로는 이렇게 적용해볼 수 있어요.
import SwiftUI
struct ContentView: View {
@ScaledMetric var imageSize: CGFloat = 100
var body: some View {
VStack(spacing: 10) {
Image("lion")
.resizable()
.frame(width: imageSize, height: imageSize)
Text("Scary Lion")
.font(.headline)
}
}
}
이렇게 ScaledMetric 프로퍼티 래퍼를 활용해 이미지 사이즈의 수치를 기준 값으로 100을 주었습니다.
그럼 초기에 기본적으로 요렇게 나타나겠죠?
여기서 시스템 설정의 접근성을 통해 텍스트 크기를 늘려볼까요?
다이나믹 타입이 타이틀에만 적용했을뿐인데 자동으로 이미지의 크기도 같이 적용되는걸 볼 수 있어요.
즉, 해당 100의 수치가 기본 값이고 만약 현재에서 텍스트 크기를 조절한다면 텍스트도 커지고 그에 따라 SacledMetric 프로퍼티 래퍼로 지정한 imageSize 값도 증가함을 볼 수 있습니다.
즉, 자연스럽죠 😃
두번째 방법으로는 특정 텍스트 스타일에 대해 상대적으로 크기를 조정하는 방법입니다.
init(
wrappedValue: Value,
relativeTo textStyle: Font.TextStyle
)
첫번째보다 초기화 파라미터 하나가 더 생겼는데요.
textStyle이 바로 상대적으로 지정할 폰트입니다.
여기에 특정 폰트를 지정하면 그 폰트에 대해 상대적인 비율로 크기를 조정하는것이죠.
여기서 중요한건 다이나믹 타입을 적용해 해당 특정 폰트가 늘어나는 비율만큼 늘거나 줄어드는 수치를 따른다는 소리입니다.
좀 이해가 안가실 수 있으니 코드로 보겠습니다!
import SwiftUI
struct ContentView: View {
@ScaledMetric(relativeTo: .headline) var headline = 100
@ScaledMetric(relativeTo: .largeTitle) var largeTitle = 100
var body: some View {
VStack(spacing: 10) {
Image("lion")
.resizable()
.frame(width: headline, height: headline)
Text("Headline")
.font(.headline)
Image("lion")
.resizable()
.frame(width: largeTitle, height: largeTitle)
Text("LargeTitle")
.font(.largeTitle)
}
}
}
두가지 비교를 위해 상대적으로 비교할 폰트를 headline과 largeTitle 폰트 스타일 두개로 별도 프로퍼티 래퍼 변수를 만들었습니다.
그리고 동일 이미지의 프레임 사이즈에 각자 변수를 적용시켰죠.
이랬을때 다이나믹 타입을 적용할때 어떤게 더 커지고 어떤 기준으로 사이즈가 조정되는지 확인해보려고 합니다 ☺️
먼저 둘다 초기값은 100이기에 앱을 실행하면 아래와 같이 같은 이미지 사이즈를 볼 수 있겠죠?
여기서 이미지는 100으로 사이즈가 같지만 폰트는 LargeTitle이 월등히 큰것을 확인할 수 있어요.
원래 폰트 사이즈 자체도 LargeTitle이 가장 큰 사이즈이고 Headline이 중간쯤 될거에요.
자 여기서 텍스트 크기를 늘려볼까요?
짜잔 ✨✨✨
이상하지 않아요?
분명 LargeTitle의 텍스트가 Headline의 텍스트보다 커졌는데, 왜 이미지는 Headline의 ScaledMetric 변수를 지정한쪽이 더 커졌을까요?
앞서 말했듯, 상대적으로 지정한 폰트의 크기 변화 비율에 있어요.
헤드라인 텍스트의 크기가 라지타이틀보다 훨씬 작아졌는데 접근성을 통해 다이나믹 타입을 극대로 올리니 이제 작긴 하지만 그렇게 차이가 안나죠?
즉, 헤드라인 텍스트의 크기 변화 비율이 라지타이틀의 크기 변화 비율보다 크다는 소리입니다 🙋🏻
결국 폰트의 절대적인 크기에 따르는것이 아니라 상대적인 폰트 크기의 변화 비율에 연관된다는 말이에요.
돌려보기전엔 당연히 라지타이틀의 변수가 적용된 이미지가 더 큰 결과를 가져오겠지!로 생각한것이 한대 맞은 느낌입니다 🥹
마무리
이렇게 ScaledMetric 프로퍼티 래퍼를 적절히 잘 활용하면 보다 더 접근성을 완벽히 고려하고 모두 대응할 수 있지 않을까요?
레이아웃도 신경쓰고 해야겠지만, 충분히 적용해볼 수 있는 가치가 있다고 생각됩니다 😃