iOS 응용 (SwiftUI)
[iOS - SwiftUI] PreferenceKey, onPreferenceChange (자식뷰에서 부모뷰로 데이터 변경 알리기, 데이터 바인딩)
jake-kim
2024. 10. 29. 01:05
PreferenceKey
- 일종의 키 이며, 이 키를 이용하여 특정 값을 변경될때마다 관찰할 수가 있음
- PreferenceKey로 데이터를 뿌리면, 이 데이터에 관심있는 쪽에서 해당 PreferenceKey로 옵저빙이 가능
- 가장 대표적인 사용 방법은 자식뷰 -> 부모뷰로 데이터를 넘길때 사용
- UIKit에서는 델리게이트나 closure로 넘기지만 SwiftUI에서는 PreferenceKey라는 것이 존재
사용방법
- ex) 자식뷰에서 TextView에 데이터가 입력될때마다 부모 뷰에 전달하고 싶은 경우?
- 자식뷰 준비
struct ChildView: View {
@State private var inputText = ""
var body: some View {
VStack {
TextField("Enter text...", text: $inputText)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
}
}
}
- inputText를 특정 키로 데이터를 방출해야하므로 키 정의 (PreferenceKey)
- PreferenceKey은 프로토콜이며, defaultValue, reduce함수를 필수로 구현해주어야함
- defaultValue는 처음 방출할 값
- reduce함수는, value가 리턴값이라 생각하면 되고, nextValue()는 최신 값
struct TypingPreferenceKey: PreferenceKey {
static var defaultValue = ""
static func reduce(value: inout String, nextValue: () -> String) {
value = nextValue()
}
}
- 이 키를 가지고, 데이터를 방출해야하므로 childView에 preference(key:value:)로 등록
- 이렇게 정의하면 위 reduce함수에서 들어오는 파라미터 nextValue()는 inputText값이 들어옴
struct ChildView: View {
@State private var inputText = ""
var body: some View {
VStack {
TextField("Enter text...", text: $inputText)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
.preference(key: TypingPreferenceKey.self, value: inputText) // <-
}
}
}
- 이 키를 가지고 옵저빙할 수 있으므로 부모뷰에서 옵저빙
- preonPreferenceChange(_:perform:) 사용
struct ContentView: View {
@State var childInput = ""
var body: some View {
VStack {
Text("childInput: \(childInput)")
ChildView()
.onPreferenceChange(TypingPreferenceKey.self, perform: { value in // <-
childInput = value
})
}
.padding()
}
}
결과)
정리
- 자식뷰 -> 부모뷰 데이터 전달 시, Binding 변수를 넘기거나 Delegate 방법도 있지만 위에서 본것처럼 PreferenceKey도 가능
- PreferenceKey의 가장 큰 특징은 전역적으로 접근이 가능 (PreferenceKey를 정의하면 이 타입만 옵저빙이 가능)
전체 코드)
import SwiftUI
struct ContentView: View {
@State var childInput = ""
var body: some View {
VStack {
Text("childInput: \(childInput)")
ChildView()
.onPreferenceChange(TypingPreferenceKey.self, perform: { value in
childInput = value
})
}
.padding()
}
}
struct ChildView: View {
@State private var inputText = ""
var body: some View {
VStack {
TextField("Enter text...", text: $inputText)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
.preference(key: TypingPreferenceKey.self, value: inputText) // <-
}
}
}
struct TypingPreferenceKey: PreferenceKey {
static var defaultValue = ""
static func reduce(value: inout String, nextValue: () -> String) {
value = nextValue()
}
}
#Preview {
ContentView()
}
* 읽어보면 좋은 글: PreferenceKey를 사용하여 ScrollView 스크롤 하단 도달했는지 확인 방법, ContentOffset 구하는 방법