티스토리 뷰
WWCD 24 > What’s new in Xcode 16 에서 소개된 preview 관련 API 두개..!
[1] @Previewable
- @Previewable 은 #Preview body 내에서 사용하는 매크로 (iOS 17+)
- 이 매크로는 #Preview body 내에서 @State property 를 선언할 수 있게 해줌
(예전에는 dynamic property 를 들고 있는 뷰의 동작을 테스트하기 위해 별도의 wrapper view 를 만들고 @State property 를 선언했음)
AS IS
import SwiftUI
struct SomeView: View {
@Binding var text: String
var body: some View {
TextField("placeholder..", text: $text)
.padding()
}
}
#Preview {
struct Preview: View {
@State var text = ""
var body: some View {
SomeView(text: $text)
}
}
return Preview()
}
TO BE
import SwiftUI
struct SomeView: View {
@Binding var text: String
var body: some View {
TextField("placeholder..", text: $text)
.padding()
}
}
#Preview {
@Previewable @State var text = ""
return SomeView(text: $text)
}
이제는 매크로가 wrapper view 를 알아서 만들어줌!
보일러플레이트 코드를 줄이고 더 간결하게 #preview 코드를 작성할 수 있게 됨.
[2] PreviewModifier
- PreviewModifier 은 preview 들끼리 데이터나 환경을 공유할 수 있게 해주는 프로토콜 (iOS 18+)
- 프로토콜의 요구사항은 다음과 같고
Context 가 Void 면 makeSharedContext 는 구현안해도 됨.
- preview system 의 캐싱으로 context 는 공유됨.
(즉 해당 type 의 modifier 을 여러군데서 써도 makeSharedContext 는 한번만 호출됨)
Create shared context to apply to previews. The context returned here will be cached and passed into the body method for every preview that applies a modifier of this type.
예제
[ environment 공유 ]
1) dynamicSize
struct LargeDynamicSizePreviewModifier: PreviewModifier {
func body(content: Content, context: Void) -> some View {
content.environment(\.dynamicTypeSize, .xxxLarge)
}
}
#Preview(traits: .modifier(LargeDynamicSizePreviewModifier())) {
@Previewable @State var text = ""
return SomeView(text: $text)
}
extension 사용하면 더 간단
struct LargeDynamicSizePreviewModifier: PreviewModifier {
func body(content: Content, context: Void) -> some View {
content.environment(\.dynamicTypeSize, .xxxLarge)
}
}
extension PreviewTrait<Preview.ViewTraits> {
static var largeDynamicSize: Self = .modifier(LargeDynamicSizePreviewModifier())
}
#Preview(traits: .largeDynamicSize) {
@Previewable @State var text = ""
return SomeView(text: $text)
}
2) decoration
struct DecorationPreviewModifier: PreviewModifier {
func body(content: Content, context: Void) -> some View {
content
.padding()
.background(.yellow)
.border(.gray)
}
}
extension PreviewTrait<Preview.ViewTraits> {
static var decoration: Self = .modifier(DecorationPreviewModifier())
}
#Preview(traits: .decoration) {
@Previewable @State var text = ""
return SomeView(text: $text)
}
[ data 공유 ]
ㄴ environment 로 namer 라는 데이터를 넘겨야하는 상황인데, 로컬파일에서 해당 데이터를 불러와서 넘길 수 있게 makeSharedContext 를 구현함.
ㄴ 이 예제는 간단하지만, preview 들간 데이터를 공유해야하는 상황에서 PreviewModifier 는 유용할 것.
(한번 불러오면 캐싱되어 데이터를 자주 불러와야하는 오버헤드도 없고 프리뷰들 끼리 동일 데이터를 공유할 수 있으므로..?)
ㄴ 문서에서는 SwiftData 의 ModelContainer 사용하는 경우를 예제로 설명하니 참고.
ㄴ 이 블로그에서 PreviewModifier 로 mock data 를 inject 하는 것의 한계를 잘 설명하니 참고.
Since we can’t make a new instance of our content, we can’t inject our mock data source directly into the view through its initializer. The only way we can get the data source to the view is by adding it to the environment.
'🍏 > SwiftUI + Combine' 카테고리의 다른 글
[SwiftUI] Layout 을 이용한 SizeLogger (2) | 2024.08.12 |
---|---|
[SwiftUI] scrollClipDisabled (0) | 2024.08.12 |
[Combine] ConnectablePublisher (0) | 2024.08.04 |
[SwiftUI] Preferences (0) | 2024.05.23 |
[SwiftUI] animation(_:body:) 로 돌발애니메이션 막기 (1) | 2023.11.30 |
- Total
- Today
- Yesterday
- PencilKit
- Flutter getter setter
- github actions
- 장고 URL querystring
- Flutter 로딩
- Flutter Clipboard
- Flutter Spacer
- 플러터 얼럿
- Flutter Text Gradient
- flutter 앱 출시
- SerializerMethodField
- 장고 Custom Management Command
- ipad multitasking
- DRF APIException
- Django FCM
- Dart Factory
- drf custom error
- Sketch 누끼
- Python Type Hint
- Watch App for iOS App vs Watch App
- ribs
- Django Heroku Scheduler
- Django Firebase Cloud Messaging
- METAL
- flutter deep link
- 플러터 싱글톤
- cocoapod
- flutter build mode
- flutter dynamic link
- 구글 Geocoding API
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |