-
Notifications
You must be signed in to change notification settings - Fork 3
SwiftUI에서 View와 Model Binding (part1: @State와 @Binding)
SwiftUI와 Combine을 이용하면서 가장 많이 보게될 propertyWrapper인
@State, @Binding @ObservedObject @Published에대해서 정리해보았습니다
예시를 들기 위해서 똑같은 View를 조금씩 다르게 구현해봤습니다
완성된 결과물은 정말 간단한 스위치입니다 ( toggle 이 on일 때만 "Hello World"를 표시합니다)
가장 간단한 방법으로는 하나의 view 내에 모든 코드를 갖고있을떄입니다
struct SampleView: View {
@State private var isOn = true
var body: some View {
VStack {
Text("This is an Example")
VStack {
Toggle(isOn: $isOn) {
Text("Show welcome message")
}.padding()
if isOn {
Text("Hello World!")
}
}
}
}
}
이 View는 true/false가 매우 중요한 view인데 이 정보를
@State private var isOn = true
에 담고 있습니다
Apple 공식 자료와 영상을 보면 이러한 값을 "source of truth"라고합니다
When that state changes, SwiftUI knows to automatically reload the view with the latest changes so it can reflect its new information.
즉 Toggle이 isOn의 값을 바꾸게되면 SwiftUI는 View의 "State"(상태)가 바뀌었다는것을 알아채서 View를 업데이트하게됩니다.
그러면 이번에는
이부분만 SubView로 따로 빼놓겠습니다
이때 Extract SubView를 쓰면 편하겠죠?
따로 SubView로 빼놨더니 이렇게 오류를 뜹니다
해당 View의 state를 표현해줄 정보가 없는것이죠
그러면 방금과 마찬가지로 @State private var isOn = true
를 추가하면 될까요?
🙅♂️안됩니다🙅♂️
이렇게하면 parent View와 child View가 각각의 "source of truth"를 가지게 되어 따로 행동하게 됩니다.
이러한 상황에 쓰이는것이 @Binding입니다
This is exactly what @Binding is for: it lets us create a property in the add user view that says “this value will be provided from elsewhere, and will be shared between us and that other place.” That property literally means “I have a Boolean value called isOn, but it’s being stored elsewhere.” -HackingWithSwift
struct SampleSubView: View {
@Binding var isOn: Bool
var body: some View {
VStack {
Toggle(isOn: $isOn) {
Text("Show welcome message")
}.padding()
if isOn {
Text("Hello World!")
}
}
}
}
그럼 이제 부모 View에서는 해당값을 넘겨줘야겠죠?
이때 주의해야할 점은 isOn이 아니라 $isOn을 넘기는것입니다.
struct SampleView: View {
@State private var isOn = true
var body: some View {
VStack {
Text("This is an Example")
SampleSubView(isOn: $isOn)
}
}
}
물론 이렇게 간단한 View일때는 @State를 써도 지장없습니다
하지만 조금더 복잡한 View, 혹은 복잡한 외부의 Model을 쓰게될때는 어떻게 해야할까요?
이때 쓰이는것이 @ObservedObject입니다
여전히 View가 해당 data에 대한 dependency가 존재하는데, 이제는 그 data를 우리가 직접 만들어주게 됩니다.
(part 2에 더 자세히 설명을 하겠지만
MVVM패턴에서는 ViewModel이 해당역할을 하게됩니다.
이해를 돕고자 코드를 보여주자면...
struct SampleModel {
var isOn: Bool
let text: String
}
class SampleViewModel: ObservableObject{
@Published var model = SampleModel(isOn: true, text: "Hello World!")
func toggleIsOn() {
model.isOn.toggle()
}
}
View는 새롭게 만든 Model과 ViewModel을 반영하기 위해
import SwiftUI
struct SampleModel {
var isOn: Bool
let text: String
}
class SampleViewModel: ObservableObject{
@Published var model = SampleModel(isOn: true, text: "Hello World!")
func toggleIsOn() {
model.isOn.toggle()
}
}
struct SampleView: View {
@StateObject var viewModel = SampleViewModel()
var body: some View {
VStack {
Text("This is an Example")
SampleSubView(isOn: $viewModel.model.isOn, sampleText: viewModel.model.text)
}
}
}
struct SwitchView_Previews: PreviewProvider {
static var previews: some View {
SampleView()
}
}
struct SampleSubView: View {
@Binding var isOn: Bool
let sampleText: String
var body: some View {
VStack {
Toggle(isOn: $isOn) {
Text("Show welcome message")
}.padding()
if isOn {
Text(sampleText)
}
}
}
}