-
SwiftUI - @State / @Binding / @StateObject / @EnvironmentObjectSwiftUI 2022. 3. 21. 20:15
안녕하세요. 그린입니다🟢
이번 포스팅에서는 전 포스팅에 이어 SwiftUI에서 구조체 내부에서 값을 변경하여 반영해주거나
전역적으로 값의 업데이트를 반영해주는 그런 친구들인 타이틀의 세친구 즉,
@State, @Binding, @StateObject, @EnvironmentObject에 대해서 학습해보겠씁니다🙋🏻
우선 하나씩 차례대로 진행해보겠습니다👍
@State
정의를 먼저보면, SwiftUI에서 값을 읽고 쓸 수 있는 유형의 어노테이션입니다.
@frozen @propertyWrapper struct State<Value>
역시 propertyWrapper이고 구조체입니다.
SwiftUI에서 우선 뷰는 구조체입니다.
즉, 내부 값을 변경해줄 수 없는데 이 @State를 이용해 변경해줄 수 있습니다.
또한 구조체이기에 이닛과 디이닛이 이뤄지기에 값의 안전함을 보장할 수 없습니다.
그럴때도 이 @State가 안전함을 보장해줍니다.
기본적으로 Private 선언이기에 다른 뷰와 값을 소통할 수 없습니다.
다른 뷰와 소통하려면 @Binding 혹은 앞서 배운 @ObservedObject 그리고 또 나올 전역적인 @EnvironmentObject를 이용해주면 됩니다.
정리해보자면 이 어노테이션은 해당 뷰에서만 이뤄지는 아주 간단한 프로퍼티에 대해서만 사용하는게 좋을것 같아요!🧑🏻⚖️
그럼 사용에 대한 공식문서의 예시를 살펴보시죠!
struct PlayButton: View { @State private var isPlaying: Bool = false var body: some View { Button(isPlaying ? "Pause" : "Play") { isPlaying.toggle() } } }
아주 간단합니다.
isPlaying이라는 @State 변수를 버튼에 매칭시켜줬습니다.
버튼이 클릭될때마다 타이틀이 변하고 isPlaying 변수 값도 토글되어 변경됩니다.
@Binding
이어서 이 어노테이션을 볼껀데요.
위에서 말했듯 @State는 자신만의 뷰에서만 변경이 이뤄지게됩니다.
그런데 SwiftUI는 뷰들의 부모/자식 뷰의 계층관계를 가질때가 많고 구조적으로 분리할 때도 많습니다.
이에 상위 뷰에서 @State의 변수가 있어 해당 값을 하위 뷰에서 캐치해 변화가 주어져야할때 사용됩니다.
일단 공식문서 정의를 보시죠!
@frozen @propertyWrapper @dynamicMemberLookup struct Binding<Value>
별거없으니 바로 사용하는 예시를 보시죠!
struct PlayButton: View { @Binding var isPlaying: Bool var body: some View { Button(isPlaying ? "Pause" : "Play") { isPlaying.toggle() } } }
struct PlayerView: View { var episode: Episode @State private var isPlaying: Bool = false var body: some View { VStack { Text(episode.title) .foregroundStyle(isPlaying ? .primary : .secondary) PlayButton(isPlaying: $isPlaying) // Pass a binding. } } }
우선 상위뷰는 PlayerView이고 하위뷰는 PlayButton입니다.
하위뷰에서 @Binding 변수를 선언해줍니다.
이 변수는 상위뷰의 isPlaying @State 변수와 바인딩됩니다.
그럼 상위뷰에서 구현부를 보시면 PlayButton을 호출하는데 인자로 @State 변수의 값을 넘겨주며 바인딩해줍니다.
그러면 하위뷰에서는 바인딩 변수가 존재함으로 상위뷰에서의 변화를 하위뷰에서도 캐치하여 감지할 수 있게됩니다.
고로 @State와 @Binding의 사용은 같이 이뤄질때가 많습니다🙌
@StateObject
이전 포스팅에서 @ObservedObject에 대해 알아본적이 있습니다!
"ObservableObject를 구독하며 값 업데이트가 발생하면 뷰를 갱신해주는 친구입니다."
라고 설명했는데요, 이와 거의 동일하다고 보시면됩니다.
즉, 관찰 가능한 객체를 인스턴스화 하는 속성 래퍼입니다.
@frozen @propertyWrapper struct StateObject<ObjectType> where ObjectType : ObservableObject
정의된것도 거의 유사해요. ObservableObject를 따르구요!
다만 차이라함은 SwiftUI의 뷰는 업데이트 될때가 많습니다.
즉 뷰의 렌더링이 일어나면 @ObservedObject의 인스턴스가 초기화될때가 있게됩니다.
이를 @StateObject는 보완해준다고 보면 될것 같아요.
위에서 @State 어노테이션을 사용해 뷰의 프로퍼티를 구성해준것과 비슷한 느낌일것 같아요🙃
@EnvironmentObject
마지막으로 이 친구에 대해 알아보시죠!
아까 소개할때 전역적인 친구라고 말했습니다.
그 이유는 여태까지 뷰 자체에서 놀거나 상위/하위뷰에서 바인딩해줘서 관리하고 했다면 이 친구는 다릅니다.
음.. 약간 싱글턴 같은 인스턴스라고 볼 수 있어요.
즉 이 어노테이션을 붙여주는 클래스 타입의 정의는 앱 전역적으로 사용될 데이터의 집합으로 이뤄지는게 좋을것 같습니다!
그럼 자세히 공식문서 정의를 보죠!
@frozen @propertyWrapper struct EnvironmentObject<ObjectType> where ObjectType : ObservableObject
ObservableObject 프로토콜을 따르군요 역시
흐름을 보자면 전역적으로 사용할 데이터에 대한 클래스를 만듭니다.
그리고 전역적 사용을 위해 SceneDelegate에서 .environmentObject(_:) 메서드를 통해 값을 매칭합니다.
마지막으로 사용될 뷰들에서 프로퍼티로 @EnvironmentObject 어노테이션을 붙인 변수를 생성합니다.
한번 예시로 보시죠!
class Info: ObservableObject { @Published var age = 10 } class SceneDelegate { ... var info = Info() window.rootViewController = UIHostingController(rootView: MainView().environmentObject(info)) ... } struct MainView: View { @EnvironmentObject var info: Info var body: some View { Button(action: { self.info.age += 1 }) { Text("Click Me for plus age") } SubView() } } struct SubView: View { @EnvironmentObject var info: Info var body: some View { Button(action: { self.info.age -= 1 }) { Text("Click Me for minus age") } } }
요렇게 간단히 쓰임을 볼 수 있을것 같아요.
먼저 앱 전역적으로 사용될 인스턴스를 위한 Info 클래스를 생성합니다.
요 안에서는 @Published 변수 age를 가지고 있어요.
이 어노테이션이 뭐라했쬬? 값의 변경이 일어나면 바로 업데이트 쳐주는! 그런 ㅎㅎ
그다음 SceneDelegate에서 전역적인 인스턴스를 만들어주고 .environmentObject(_:) 메서드를 이용해 해당 인스턴스를
담아줍니다.
그다음 메인뷰와 서브뷰를 볼 수 있어요.
해당 인스턴스 타입의 @EnvironmentObject 변수를 만들어줍니다.
그리고 이에 대해 각 역할을 부여해주면 됩니다.
요러면 끝...! 쉽죠??
마무리
자 이렇게 이전과 오늘에 연결되어 Combine과 SwiftUI에서의 뷰와 데이터 변화와 흐름을 이해하고 사용하는것에 대해 알아봤습니다!
써봐야 감이 잘 올거에요!
그럼 긴말 필요없이 써봅시다🙌
[참고자료]
https://developer.apple.com/documentation/swiftui/state
https://developer.apple.com/documentation/swiftui/binding
https://developer.apple.com/documentation/swiftui/stateobject
https://developer.apple.com/documentation/swiftui/environmentobject
'SwiftUI' 카테고리의 다른 글
SwiftUI 4.0 - Charts (0) 2022.06.10 SwiftUI - TabView (0) 2022.04.09 Custom Stepper (0) 2022.01.28 SwiftUI - multilineTextAlignment & lineLimit & lineSpacing (0) 2022.01.19 swipeActions (0) 2021.12.23