SwiftUI 프로퍼티 래퍼
SwiftUI를 사용할 때 데이터를 다른 뷰에서 사용하거나 입력을 받기 위해서 프로퍼티 래퍼를 사용한다 프로퍼티 래퍼의 사전적 의미(사용)은 뷰의 상태를 관리하고, 데이터 바인딩을 제공하며, 앱 환경을 뷰계층에 전달하는 역할을 한다고 한다
State 프로퍼티 래퍼
@State는 뷰의 로컬 상태를 관리하는데 사용 이 데이터가 변경되면 뷰는 재랜더링 된다 State는 보통 뷰 내부에서만 사용되고 뷰 외부로 상태를 전달하거나 공유할 수 없다
- 용도: 뷰가 다시 랜더링되어도 유지되어야 하는 로컬 상태를 관리할 때 사용한다
- 수명: 뷰와 함께 지속되며 뷰가 사라지면 상태도 같이 사라진다
1
2
3
4
5
6
7
8
9
10
11
12
struct CounterView: View {
@State private var count = 0 // 로컬 상태인 count를 @State로 선언
var body: some View {
VStack {
Text("Count: \(count)")
Button("Increase") {
count += 1 // 버튼을 누를 때마다 상태가 업데이트되고, 뷰가 다시 렌더링됨
}
}
}
}
Binding 프로퍼티 래퍼
Binding은 부모의 뷰의 상태를 자식 뷰에서 공유하고 수정할 수 있게 해준다 자식뷰에서 부모의 상태에 직접 접근 또는 수정이 가능하다
- 용도: 상위 뷰에서 관리하는 상태를 하위 뷰에서 수정하거나 접근 할 때 사용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct ParentView: View {
@State private var isOn = false // 부모 뷰의 상태
var body: some View {
ToggleView(isOn: $isOn) // 자식 뷰로 상태를 바인딩하여 전달
}
}
struct ToggleView: View {
@Binding var isOn: Bool // 부모 뷰에서 전달된 상태에 바인딩
var body: some View {
Toggle("Switch", isOn: $isOn) // 바인딩된 상태로 Toggle 제어
}
}
여기서 관리할 상태는 state로 선언하고 부모 뷰에서 하위뷰로 연결할 때 하위뷰로 이 값을 전당해줘야 한다 그리고 받은 상태를 하위뷰에서 Binding 해서 이용할 수 있다
ObservedObject 프로퍼티 래퍼
외부 객체에서 상태를 관찰하고 그 객체의 상태가 변경될 때 뷰를 다시 랜더링한다 클래스로 선언ehlsl 객체 모델에서 사용되며 그 클래스는 ObservableObject 프로토콜을 이용해서 선언해야한다 구조체는 안되고 클래스로 만들어야한다
- 용도: 뷰 외부에 있는 객체를 감시하고 그 객체가 변하면 뷰를 재랜더링한다
- 요구사항: 클래스로 선언할 때 ObservableObject 프로토콜을 준수해야하고 클래스 내부 변수는
@Published프로퍼티로 선언한다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class CounterModel: ObservableObject {
@Published var count = 0 // 상태를 관찰할 수 있도록 @Published로 선언
}
struct CounterView: View {
@ObservedObject var model = CounterModel() // 모델 객체를 관찰
var body: some View {
VStack {
Text("Count: \(model.count)")
Button("Increase") {
model.count += 1 // 모델 객체의 상태가 변경되면 뷰가 다시 렌더링됨
}
}
}
}
StateObject 프로퍼티 래퍼
방금 전에 쓴 ObservedObject 프로퍼티와 비슷한 역할을 한다 하지만 생성과 삭제가 다르다 ObservedObject는 뷰가 없어져도 객체는 유지되지만 StateObject는 뷰가 생성될때 생성되고 뷰가 사라지면 객체도 사라진다
@StateObject와 @ObservedObject의 차이점
@StateObject: 뷰에서 객체를 처음으로 생성하고 그 객체의 상태를 관리 뷰가 새로 생성될 때마다 새로운 객체가 생성되며, 해당 객체는 뷰의 수명 주기에 맞춰 관리@ObservedObject: 이미 다른 곳에서 생성된 객체를 관찰할 때 사용 객체의 생성을 뷰 외부에서 관리하고, 해당 객체의 상태를 변경 사항에 따라 감시
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class CounterModel: ObservableObject {
@Published var count = 0 // @Published를 통해 상태 변화를 알림
}
struct CounterView: View {
@StateObject var model = CounterModel() // @StateObject로 모델을 뷰에서 직접 관리
var body: some View {
VStack {
Text("Count: \(model.count)")
Button("Increase") {
model.count += 1
}
}
}
}
이렇게 뷰에서 프로퍼티 래퍼를 작성할 때 만 다르고 사용 방법을 같다
차이점 – 코드 –
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class CounterModel: ObservableObject {
@Published var count = 0
}
struct ParentView: View {
@StateObject var model = CounterModel() // 객체를 생성하고 상태를 관리
var body: some View {
ChildView(model: model) // 자식 뷰에 상태 전달
}
}
struct ChildView: View {
@ObservedObject var model: CounterModel // 부모 뷰에서 전달된 객체를 관찰
var body: some View {
VStack {
Text("Count: \(model.count)")
Button("Increase") {
model.count += 1
}
}
}
}
이렇게 사용한다면 Binding 없이도 부모에게서 변수(상태)를 전달 받을 수 있다
EnvironmentObject
@EnvironmentObject는 앱으니 전역 상태를 공유하고 여러 뷰에서 쉽게 접근 할 수 있다 부모자식 관계를 넘어서 광범위하게 상태 공유 가능
- 용도 여러 뷰에서 전역적으로 상태를 공유
- 요구사항: 객체는 EnvironmentObject 프로퍼티 래퍼를 사용해야한다
| 프로퍼티 래퍼 | 설명 | 주요 용도 |
|---|---|---|
| @State | 뷰의 로컬 상태를 관리하며, 상태 변경 시 자동으로 뷰를 다시 렌더링 | 뷰 내부에서 사용되는 상태 관리 |
| @Binding | 상위 뷰에서 상태를 바인딩하여 하위 뷰에서 접근하고 수정할 수 있게 함 | 상위 뷰의 상태를 하위 뷰에서 수정 |
| @ObservedObject @StateObject | 외부에서 상태를 관리하는 객체를 관찰하며, 상태가 변경되면 뷰를 다시 렌더링 | 외부 모델 객체의 상태를 감시하고 변경 사항 반영 |
| @EnvironmentObject | 앱 전역에서 상태를 공유하며, 여러 뷰에서 동일한 객체에 접근할 수 있음 | 전역 상태 공유 및 여러 뷰 간 상태 동기화 |