@State
struct parent: view {
@State var isToggle: Bool = true
var body: some View {
Toggle(isOn: $isToggle) {
if isToggle {
Text("on")
}
}
}
}
SwiftUI
가 관리하는 프로퍼티 값을 읽고 쓸수 있게 하는 프로퍼티 래퍼
@State
로 선언된 값이 변경되면 뷰는 변경된 값으로 body를 다시 계산한다. → 뷰는 항상 최신 값을 가진다.
isToggle : Bool
,$isToggle: Binding<Bool>
이다. → $isToggle로 받으면 WrapperValue 자체를 수정하므로 해당 값이 변경될 때에 View를 새로 그린다.
- 특정 View의 상태를 저장하기 위해 만들었기 때문에 보통
private
이다. → 자식 뷰에서@State
를 통해 자식 뷰를 업데이트 해도 부모 뷰에 영향을 끼치지 않는다.
- 두 개 이상의 뷰가 동시에 하나의
@State
를 참조할 때에@Binding
을 통해서만 가능하다.
@Binding
struct child: View {
@Binding var isToggle: Bool = true
var body: some View {
Toggle(isOn: isToggle) {
if isToggle {
Text("on")
}
}
}
}
// @State 예시의 코드를 다음과 같이 수정
...
var body: some View {
child(isToggle: $isToggle)
}
- 자식 뷰에서
@Binding
값이 변경되면 부모의@State
값도 변경된다. → 뷰 간의 양방향 연결
- 외부에서 접근하므로 private가 아니다.
@ObservableObject
class ViewModel: ObservableObject {
@Published var count: Int = 0
...
func addCount() {
count += 1
}
}
// 해당 클래스를 사용할 때
@ObservedObject var viewModel = ViewModel()
- 관측 가능한 개체. 프로퍼티가 아니라 class를 바인딩 할 때에는
ObservableObject
키워드로 class를 선언하고@ObservedObject
키워드로 사용한다.
ObservableObject
에서@Publised
키워드로 선언된 값이 변경될 때에 이를 View에게 알리고, 이를 바탕으로 View를 업데이트 한다.
이상한 문제
// 부모 뷰
struct parent: View {
@State var count = 0
var body: some View {
VStack {
child(parentCount: count)
Button {
self.count += 1
} label: {
Text("add parent")
}
}
}
}
// 자식 뷰
struct child: View {
@ObservedObject var viewModel = ViewModel()
var parentCount = 0
var body: some View {
VStack {
Text("parent: \(parentCount)")
Text("child: \(viewModel.count)")
Button {
viewModel.add()
} label: {
Text("add")
}
}
}
}
// 뷰모델
class ViewModel: ObservableObject {
@Published var count = 0
func add() {
count += 1
}
}
- parent 뷰에서
@State
로 선언된 count 값이 변경되면 View를 refresh 한다. 이 과정에서 child의 view모델이 초기화 되는 문제가 발생.
- 해결방법
1. child의 parentCount 값을 부모의 count 프로퍼티에서 참조할 수 있도록
@Binding
형태로 변경 2. child의 view모델을@StateObject
로 객체화 한다.
@StateObject (iOS 14.0+)
@StateObject var viewModel = ViewModel()
@ObservedObject
는 view의 라이프 사이클에 의해 메모리가 관리되지만@StateObject
는 별도의 메모리 공간을 만들어 View와 별도로 메모리가 관리된다.
ObservebleObject
를 처음 초기화 할때에는@StateObject
를, 이미 객체화 된 것을 넘겨받을 때에는@ObservedObject
를 사용하기를 추천한다고 한다.
@EnvironmentObject
- 따로 명시하지 않아도 부모나 조상 뷰와 함께 적용되는 객체.
- @main 에서 설정해두면 앱 어디서든 사용할 수 있다.
Reference
Uploaded by N2T