SwiftUI에서는 @State, @StateObject와 같은 프로퍼티 래퍼로 뷰 내부, 뷰 사이에 데이터 공유를 할 수 있다.
@State
현재 뷰에서 비교적 간단한 데이터 공유(실시간 데이터 변화 감지 등)를 위해 사용한다. 뷰들 사이에 데이터 공유는 할 수 없다. 또 Struct 형태만 변수로 사용 가능하다.
아래코드를 보면 Score struct를 만들어서 뷰 내부에서 공유할 데이터 구조를 만들었고, Content 뷰 안에 score변수에서 Score struct의 인스턴스를 @State 프로퍼티 래퍼로 선언하였다. 텍스트 필드에서 내용을 수정하면 실시간으로 텍스트에 반영이 되고, 버튼을 클릭하면 isScore 값이 변경되어 텍스트 메시지도 바뀐다.
import SwiftUI
struct Score {
var playerName: String = "sonny"
var isScore: Bool = true
}
struct ContentView: View {
@State private var score = Score()
var body: some View {
VStack {
if score.isScore {
Text("\(score.playerName)가 골을 넣었다.")
.font(.title)
.fontWeight(.bold)
} else {
Text("\(score.playerName)가 골을 못 넣었다.")
.font(.title)
.fontWeight(.bold)
}
TextField("선수 이름을 입력하세요", text: $score.playerName)
Button("슛") {
score.isScore.toggle()
}
.padding(.horizontal, 30)
.padding(.vertical, 10)
.foregroundColor(Color.white)
.background(Color.mint)
.cornerRadius(20)
}
.padding(.horizontal, 30)
}
}
@State를 이용하면 struct를 이용해서 간단하게 뷰 내부의 값을 공유할 수 있다.
하지만 @State 프로퍼티 래퍼로는 클래스 데이터 구조를 사용할 수 없다. 위 예제코드에서 Score struct를 class로 바꾸면 아래와 같이 텍스트 필드의 내용을 바꾸거나 버튼을 눌러도 텍스트의 내용이 바뀌지 않는 것을 볼 수 있다.
class를 사용해야 할 때는 @StateObject, @Published 프로퍼티래퍼, Observable 프로토콜을 사용해야 한다.
@StateObject
맨 위의 코드에서 Score struct를 class로만 변경한 코드이다. 이대로는 위에서 말한 것처럼 제대로 동작하지 않는다.
class Score {
var playerName: String = "sonny"
var isScore: Bool = true
}
struct ContentView: View {
@State private var score = Score()
var body: some View {
VStack {
if score.isScore {
Text("\(score.playerName)가 골을 넣었다.")
.font(.title)
.fontWeight(.bold)
} else {
Text("\(score.playerName)가 골을 못 넣었다.")
.font(.title)
.fontWeight(.bold)
}
TextField("선수 이름을 입력하세요", text: $score.playerName)
Button("슛") {
score.isScore.toggle()
}
.padding(.horizontal, 30)
.padding(.vertical, 10)
.foregroundColor(Color.white)
.background(Color.mint)
.cornerRadius(20)
}
.padding(.horizontal, 30)
}
}
다른 뷰들에게 score class의 프로퍼티의 값이 변경되었다고 알려주기 위해서 @Published 프로퍼티 옵저버를 사용해야 한다. 프로퍼티 옵저버는 프로퍼티 값의 변화를 관찰하는 것으로 새 값이 설정되면 호출된다.
// Score 클래스 프로퍼티의 변화를 알려주기 위한 @Published 프로퍼티 옵저버 작성
class Score {
@Published var playerName: String = "sonny"
@Published var isScore: Bool = true
}
이 방법만 사용하는 것으로 class의 프로퍼티를 사용할 수 있으면 좋겠지만 몇 가지 세팅이 더 필요하다. @State 역할(SwiftUI의 뷰를 리로드하라고 알려주는)을 대신 하는 프로퍼티 래퍼를 작성해주어야 하는데 그것이 바로 @StateObject 프로퍼티 래퍼이다. private은 있어도 작동하고 없어도 작동하지만, 다른 뷰와 데이터를 공유하려고 할 때는 private을 없애야 한다.
// @StateObject 설정
@StateObject private var score = Score()
마지막으로 할 작업은 class에 Obeservable 프로토콜을 준수하라고 작성하는 것이다. @StateObject 프로퍼티 래퍼는 Observable 프로토콜을 준수하는 class에 대해서만 사용할 수 있다. 하지만 일반적인 다른 프로토콜과는 다르게 Observable 프로토콜을 작성한다고 해서 추가적으로 내용을 작성해야 하는 것은 아니며, 변화를 모니터링하기를 원한는 class라는 의미라고 생각하면 된다.
그래서 최종적으로 뷰 들 사이에 class 형식의 데이터를 공유하기 위해서 최종 코드 형태는 아래와 같다.
import SwiftUI
class Score: ObservableObject {
@Published var playerName: String = "sonny"
@Published var isScore: Bool = true
}
struct ContentView: View {
@StateObject private var score = Score()
var body: some View {
VStack {
if score.isScore {
Text("\(score.playerName)가 골을 넣었다.")
.font(.title)
.fontWeight(.bold)
} else {
Text("\(score.playerName)가 골을 못 넣었다.")
.font(.title)
.fontWeight(.bold)
}
TextField("선수 이름을 입력하세요", text: $score.playerName)
Button("슛") {
score.isScore.toggle()
}
.padding(.horizontal, 30)
.padding(.vertical, 10)
.foregroundColor(Color.white)
.background(Color.mint)
.cornerRadius(20)
}
.padding(.horizontal, 30)
}
}
class 를 통해 우리의 상태(데이터 등)를 외부객체에 저장할 수 있고, 이를 통해 뷰들 간에 같은 값을 가리키게 할 수 있다는 것이다.
'Apple > SwiftUI' 카테고리의 다른 글
SwiftUI 05 - @Binding 사용법 (1) | 2022.03.26 |
---|---|
SwiftUI03 - Stepper (0) | 2022.03.14 |
SwiftUI02 - Button (0) | 2022.03.14 |
SwiftUI01 - Stack(HStack, VStack, ZStack) (0) | 2022.03.14 |