SwiftUI ) @State, @ObservedObject, @EnvironmentObject
안녕하세요 :) Zedd입니다.
오늘은 드디어..."그것"들을 공부해보려고 합니다.
State
https://developer.apple.com/documentation/swiftui/state
- SwiftUI는 state로 선언한 모든 프로퍼티의 스토리지를 관리.
- state 값이 변경되면 view가 appearance를 invalidates하고 body를 다시 계산(recomputes)합니다.
- 주어진 view에서 state를 single source of truth로 사용 할 것.
- state인스턴스는 value자체가 아님. 값을 읽고 변경하는 수단. state의 기본값에 접근하려면 value 프로퍼티를 사용 할 것.
- view의 body에서만 state프로퍼티에 접근 할 것. 따라서 view의 클라이언트에서 state에 접근하지 못하도록 state프로퍼티를 private으로 선언할 것. (state는 특정 view에 속하고, view 외부에서 "절대" 사용되지 않은 간단한 프로퍼티에 적합 -> 해당 상태가 절대로 escape되지 않도록 설계되었다는 것을 강조하기 위해 private으로 표시하는 것이 중요함.)
- @State를 앞에 추가하면 SwiftUI가 자동으로 변경사항을 observe하고 해당 state를 사용하는 view부분을 업데이트.
예를 들어봅시다.
SwiftUI의 segmentedControl.
저 selection 파라미터가 binding을 받습니다.
근데 문제는 저기에 constant(0)이 들어가있습니다.
constant라는 이름에서 감이 오듯이 selection이 0(=A)에서 바뀌질 않음.
B나 C를 아무리 탭해도 SegmentedControl이 움직이는 일이 없습니다.....
현재 selection된 "상태"를 변경하고 해당 state가 저 segmentedControl을 다시 그려야하는데요,
이럴 때 @State로 프로퍼티를 하나 선언하면 됩니다.
이렇게요!
selection파라미터에 Binding타입이 들어갈 수 있으므로,
특정 state를 selection에 binding할 수 있습니다.
$연산자를 이용해서 바인딩을 얻을 수 있어요.
ObservedObject
https://developer.apple.com/documentation/swiftui/observedobject
ㅇㅏ니 정의 없는거 뭔데
구글링하겠읍니다...
@State같은 경우에는 특정 view에서만 사용하는 프로퍼티였다면 ObservedObject는
- 더 복잡한 프로퍼티(여러 프로퍼티나 메소드가 있거나, 여러 view에서 공유할 수 있는 커스텀 타입이 있는 경우) 대신 @ObservedObject를 사용.
- String이나 integer같은 간단한 로컬 프로퍼티대신 외부 참조 타입(external reference type)을 사용한다는 점을 제외하면 @State와 매우 유사.
- @ObservedObject와 함께 사용하는 타입은 ObservableObject프로토콜을 따라야함.
- observed object가 데이터가 변경되었음을 view에 알리는 방법은 여러가지가 있지만 가장 쉬운 방법은 @Published 프로퍼티 래퍼를 사용하는 것. = SwiftUI에 view reload를 트리거.
여기 예제가 제일 간단하고...그런 듯 ㅎㅎ
UserSettings의 score앞에 @Published가 붙었죠?
아까 말했듯이 @Published가 붙었기 때문에 이 score가 변경되면 view를 reload하게 됩니다.
+ ) 참고로 왜 UserSetting이 class지? struct면 안되나 싶을 수 있는데 ObservableObject가 class-bound 프로토콜입니다!
EnvironmentObject
https://developer.apple.com/documentation/swiftui/environmentobject
이건 다행히 정의가 있네요.
- 바인딩 가능한 객체가 변경될 때 마다 현재 view를 invalidate하기 위해 상위 view(ancestor view)에서 제공한 Binding가능한 객체를 사용하는 dynamic view property.
- 반드시 environmentObject (_ :) 메소드를 호출하여 상위 뷰에서 모델 객체를 설정해야함.
아니 정의 진짜 개같내
뭐라는거
@EnvironmentObject는
한 문장으로 말할 수 있을 것 같아요
"it’s shared data that every view can read if they want to."
모든 view가 읽을 수 있는 shared data.
아까 예제를 가지고 한번 해볼게요.
아까 만들어놓은 UserSettings이 있었죠.
아까 정의에서도 말했듯이,
반드시 environmentObject (_ :) 메소드를 호출하여 상위 뷰에서 모델 객체를 설정해야합니다.
SceneDelegate의 scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions)에 가서
"environmentObject (_ :) 메소드를 호출하여 상위 뷰에서 모델 객체를 설정"해줍니다.
그리고 이제 UserSetting을 쓰는 쪽에서
@EnvironmentObject를 선언해줍니다. UserSettings타입으로요.
저는 아까 딱 거기에서
ZeddView로 이동할 수 있게 했어요. ZeddView역시
@EnvironmentObject를 선언해줍니다.
그럼
제가 ContentViews쪽에서 score를 올리고 ZeddView에 가면 올린 score가 나와야겠죠!?
짜잔
물론 같은 기능?을 @ObservedObject로도 구현할 수 있지만...
아무튼 지금 중요한건
@EnvironmentObject로 선언하면 앱의 어느곳에서나 공유할 수 있는 데이터가 된다는 것이죠!
모델이 변경되면 이 EnvironmentObject가 바인딩되고 있는 모든곳은
view가 자동으로 업데이트되는게 진짜 좋은 것 같아요
참고
https://developer.apple.com/documentation/swiftui/state