iOS 개발 기록

[SwiftUI]데이터 바인딩 본문

SwiftUI

[SwiftUI]데이터 바인딩

택꽁이 2023. 3. 3. 13:20
728x90
📄

@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

EnvironmentObject | Apple Developer Documentation
A property wrapper type for an observable object supplied by a parent or ancestor view.
https://developer.apple.com/documentation/swiftui/environmentobject
SwiftUI 튜토리얼 5편 — @State, @Binding, @ObservedObject
다양한 바인딩 방법에대해 알아보기
https://medium.com/harrythegreat/swiftui-튜토리얼-5편-state-binding-observedobject-83c00c3317cb

Uploaded by N2T