swiftUI 공식 문서 중 State and Data Flow 섹션에 있는 문서들을 읽어보고 정리해보려 합니다 :)
https://developer.apple.com/documentation/swiftui/state-and-data-flow
상태 및 데이터 흐름
앱의 모델 내에서 데이터 흐름과 변경 사항을 제어하고 이에 대응합니다.
swiftUI 에서 상태 및 데이터 관리를 어떻게 하는지를 다뤄놓은 문서라고 할 수 있습니다 :)
swiftUI 는 사용자의 action으로 어떤 state를 변화 시키고 이 state와 관련있는 view를 자동으로 업데이트 해줍니다.
저는 이걸보고 reactorkit이 떠오르더라구요.
그래서 reactorkit 공식문서에서 이미지(오른쪽) 를 가져와봤는데 정말 유사하다는 것을 확인하실 수 있습니다.
swiftUI에서는 이런 state를 다룰 수 있는 상태 변수를 위한 도구를 제공하고 있습니다 :)
해당 문서는 다양한 서브 섹션들을 담고 있는데, 차차 하나씩 꼼꼼하게 살펴볼 예정입니다.
오늘은 이 중 View State 쪽을 살펴보려 합니다
Store data as state in the least common ancestor of the views that need the data to establish a single
source of truth that’s shared across views. Provide the data as read-only through a Swift property, or create a two-way connection to the state with a binding. SwiftUI watches for changes in the data, and updates any affected views as needed. Don’t use state properties for persistent storage because the life cycle of state variables mirrors the view life cycle. Instead, use them to manage transient state that only affects the user interface, like the highlight state of a button, filter settings, or the currently selected list item. You might also find this kind of storage convenient while you prototype, before you’re ready to make changes to your app’s data model.
- property 를 이용한 읽기 전용 / binding을 이용한 양방향 커넥션을 제공
- 영구 저장소에 상태 속성을 사용하지 말 것 !
- 사용자 인터페이스에 영향을 미치는 일시적인 상태를 관리하는 데에만 사용할 것
위의 그림에서와 같이 swiftUI View의 State와 하위 뷰들을 연결시키는 방법에는 2가지가 있습니다.
1. Binding을 통한 양방향 connection
2. property를 이용한 read-only
두 가지에 대한 예제를 보여드리겠습니다 :)
struct ContentView: View {
@State var isLoading: Bool = false
var body: some View {
VStack(spacing: 20) {
Text (isLoading ? "로딩이다" : "로딩아니다")
Button(action: {
self.isLoading.toggle()
}) {
Circle()
.frame(width: 20, height: 20, alignment: .center)
.foregroundColor(isLoading ? .red : .green)
}
}
}
}
이런 뷰를 예시로 만들어보았습니다 :)
해당 뷰는 isLoading이라는 state를 가지고 있고 동그란 button이 선택되면 해당 상태가 변경됩니다 .
struct ContentView: View {
@State var isLoading: Bool = false
var body: some View {
VStack(spacing: 20) {
Text (isLoading ? "로딩이다" : "로딩아니다")
Button(action: {
self.isLoading.toggle()
}) {
Circle()
.frame(width: 20, height: 20, alignment: .center)
.foregroundColor(isLoading ? .red : .green)
}
LoadingButton(isLoading: $isLoading)
}
}
}
struct LoadingButton: View {
@Binding var isLoading: Bool
var body: some View {
Button(action: {
self.isLoading.toggle()
}) {
Text(isLoading ? "로딩중" : "버튼")
.foregroundColor(.black)
.background(isLoading ? .gray : .clear)
}
}
}
그리고 하위에 LoadingButton을 만들어보았습니다.
그리고 상위 뷰의 isLoading State를 LoadingButton 의 isLoading와 바인딩을 시켜주었습니다 :)
이 때에는 dollar sign ($)을 붙여야 합니다
어떤 버튼이 눌렸는지 식별이 조금 어려운 것 같네요 ㅠ_ㅠ
살짝 반짝이면서 바뀌는 버튼이 눌렸다고 생각해주시면 좋을 것 같습니다
이렇게 상위 뷰위 버튼이 눌려도 하단 버튼의 ui 상태가 바뀌고,
LoadingButton 의 버튼이 눌려도 상위 뷰의 ui 상태도 같이 변화하는 것을 볼 수 있습니다.
2번째 방식은 property에 값을 넣는 방식입니다.
struct ContentView: View {
@State var isLoading: Bool = false
var body: some View {
VStack(spacing: 20) {
Text (isLoading ? "로딩이다" : "로딩아니다")
Button(action: {
self.isLoading.toggle()
}) {
Circle()
.frame(width: 20, height: 20, alignment: .center)
.foregroundColor(isLoading ? .red : .green)
}
LoadingButton(isLoading: isLoading)
}
}
}
struct LoadingButton: View {
var isLoading: Bool
var body: some View {
Button(action: {
}) {
Text(isLoading ? "로딩중" : "버튼")
.foregroundColor(.black)
.background(isLoading ? .gray : .clear)
}
}
}
이렇게 하위 뷰의 property와 state를 연결할 수도 있습니다.
상위 뷰의 state가 변경되면 하위 뷰에도 영향을 끼치는 것을 볼 수 있습니다 :)
여기에서는 하위 button에서 toggle을 해주지 않았는데
swiftUI 내에서 해당 property의 값을 변경해주려면 @State 상태로 선언해주어야하기 때문입니다.
그럼 하위 뷰에서 해당 값을 @State로 선언해주면 어떻게 될까요 ?
struct ContentView: View {
@State var isLoading: Bool = false
var body: some View {
VStack(spacing: 20) {
Text (isLoading ? "로딩이다" : "로딩아니다")
Button(action: {
self.isLoading.toggle()
}) {
Circle()
.frame(width: 20, height: 20, alignment: .center)
.foregroundColor(isLoading ? .red : .green)
}
LoadingButton(isLoading: isLoading)
}
}
}
struct LoadingButton: View {
@State var isLoading: Bool
var body: some View {
Button(action: {
}) {
Text(isLoading ? "로딩중" : "버튼")
.foregroundColor(.black)
.background(isLoading ? .gray : .clear)
}
}
}
이렇게 하니 또 다르네요 !
처음 화면이 세팅될 때에는 상위 뷰의 State 값을 가지고 세팅하지만
그 이후에는 각자의 State 값에 따라 변경되는 것을 확인하실 수 있습니다 :) (서로 영향 x)
막연하게 알고있던 @State 와 @Binding 개념에 대해서 정리해보았습니다 :)
다음 글에서는 ObservedObject에 대해 알아보겠습니다.
'iOS > swiftUI' 카테고리의 다른 글
[swiftUI] State and Data Flow - [2탄] (0) | 2022.07.24 |
---|---|
[swiftUI] GeometryReader - 화면비율에 따라 UI 조절 (0) | 2022.02.28 |
[swiftUI] 스위프트UI 시작하기 - 선언형 UI 가 뭘까? (0) | 2022.01.16 |