Post

SwiftUI 상태 프로퍼티, Observable, State, Environment 객체

SwiftUI 상태 프로퍼티, Observable, State, Environment 객체

상태 프로퍼티, state 객체, observable 객체, environment 객체를 이용해서 앱의 상태를 추적하고 업데이트 하는 데 사용되며 화면이 새로 그려질 때 변경 사항을 반영하는 역할을 한다 이 4가지 객체는 모두 사용자 인터페이스의 모양과 동작을 결정하는 상태를 제공한다

상태 프로퍼티

상태 프로퍼티는 상태에 대한 가장 기본적인 형태이다 현재 상태(버튼이 활성화 되었는지)를 저장하기 위해서만 사용된다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import SwiftUI

struct ContentView: View {
    @State private var toggle: Bool = false
    @State private var text: String = ""
    
    var body: some View {
        VStack {
            Toggle(isOn: $toggle) {
                Text(toggle ? "On" : "Off") // 토글 표시
            }
            TextField("Enter text", text: $text) //상태 프로퍼티 바인딩
            Text(text) // 프로터티가 $ 없이 사용
                .border(Color.red, width: 1)
        }
        
    }
}

@State 프로퍼티 래퍼로 선언하고 상태값은 해당 뷰에 속한것이기 때문에 private으로 선언한다 상태 프로퍼티에 바인딩하기 위해서 프로퍼티 앞에 $를 붙이면 된다 토글이나 글자를 입력하면 상태 프로퍼티가 업데이트 되고 화면이 다시 렌더링된다 이 후 프로퍼티 값을 사용할려면 $를 붙이지 않아도 사용할 수 있다 왜냐하면 상태 프로퍼티에 할당된 값을 사용하기 때문이다

 

State 바인딩

상태 프로퍼티는 선언된 뷰에서 사용이 가능한다 선언 외부에 있는 하위뷰에서 해당 상태 프로퍼티를 사용할려면 선언이 되어있지 않은 변수기 때문에 사용이 불가능하다 이때 @Binding을 사용하면 된다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
struct ContentView: View {
    @State private var toggle: Bool = false
    @State private var text: String = ""
    
    var body: some View {
        VStack {
            Toggle(isOn: $toggle) {
                Text(toggle ? "On" : "Off")
            }
            TextField("Enter text", text: $text)
            Text(text)
                .border(Color.red, width: 1)
          	newView(text: $text) // 바인딩 값 전달
        }
        
    }
}

struct newView: View {
    @Binding var text: String //이게 없다면 아래 오류 발생
    var body: some View {
        Text(text + "Hello, World!") //Cannot find 'text' in scope 오류
    }
}
#Preview {
    ContentView()
}

 

Observable 객체

상태 프로퍼티에서는 상태를 해당 뷰에서만 사용할 수 있다 하위 뷰가 아니거나 state 바인딩이 구현되어 있지 않은 다른 뷰에는 접근할 수 없다 observable 객체는 여러 다른 뷰들이 외부에서 접근할 수 있는 지속적인 데이터를 표현하기 위해 사용된다 상태가 변경되면 모든 구독하는 View에 알린다

1
2
3
4
5
6
7
8
9
10
11
// test.swift
import SwiftUI
import Combine

class ViewModel: ObservableObject {
    @Published var count: Int = 0
    
    func increment() {
        count += 1
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// contentview.swift
import SwiftUI
import Combine

struct ContentView: View {
    @ObservedObject var viewModel = ViewModel()
    
    var body: some View {
        Button("Increment") {
            viewModel.increment()
        }
        Text("Count: \(viewModel.count)")
    }
}
#Preview {
    ContentView()
}

increment를 누르번 숫자가 증가한다

 

State 객체

@ObservedObject 객체와 비슷하지만 객체를 소유하는것이 선언한 뷰가 소유하지 않고 참조를 선언한 뷰가 참조를 소유한다

구분@StateObject@ObservedObject
역할View가 객체를 직접 생성하고 생명주기를 관리외부에서 전달된 객체를 관찰 및 구독
생명주기 관리View가 객체의 생명주기를 관리함View는 객체의 생명주기를 관리하지 않음
사용 시기View에서 객체를 처음 생성하고 사용할 때외부에서 생성된 객체를 View에서 감시할 때
객체의 소유권View가 객체를 소유하고 초기화객체는 외부에서 생성되며, View는 소유하지 않음
재생성 여부View가 새로 생성될 때 객체도 새로 생성됨외부에서 생성된 객체를 계속 감시
사용 예시View가 자체적으로 상태를 관리해야 하는 경우View 외부에서 관리되는 상태를 공유해야 하는 경우

 

Environment 객체

앱 전반에 걸쳐 데이터를 공유하는 데 사용되며 부모 View로부터 하위 View로 전달된 객체를 자동으로 구독한다 앱의 전체적인 상태나 설정을 여러 View에서 참조할 때 사용한다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// ContnetView.swift
import SwiftUI
import Combine

struct ContentView: View {
    @StateObject var settings = UserSettings()  // UserSettings 객체를 직접 생성하고 관리
        
        var body: some View {
            ChildView()
                .environmentObject(settings)// EnvironmentObject로 하위 뷰들에 전달
        }
}

class UserSettings: ObservableObject {
    @Published var username: String = "Guest"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// ChildView.swift
import SwiftUI

struct ChildView: View {
    @EnvironmentObject var settings: UserSettings  // 부모 View에서 제공된 전역 상태를 사용
    
    var body: some View {
        VStack {
            Text("Hello, \(settings.username)")  // 상태에 접근하여 UI에 반영
            Button("Change Username") {
                settings.username = "SwiftUI User"  // 상태를 변경
            }
        }
    }
}

여기서 UserSettings를 ObservableObject로 선언해서 전역적으로 관리할 수 있게하고 @Published를 통해서 변화를 알릴수 있게 한다 또한 .environmentObject(settings)를 통해서 하위 뷰들에게 전역 상태를 전달한다

 

SwiftUI에서 상태 프로퍼티는 현재 레이아웃 내 뷰 상태를 저장하는데 사용하고 이 값은 임시적이라 해당 뷰가 사라지면 값도 없어진다 하위 뷰에 필요한 데이터는 observable객체 프로퍼티를 사용해야한다 이 방법을 사용한다면 클래스나 구조체는 ObservableObject 프로토콜을 따라야하고 @Published 프로퍼티 래퍼를 사용하여 선언해야 한다 뷰 선언부에 Observable 객체 프로퍼티와 바인딩하려면 @ObservedObject 또는 @ StateObject 프로퍼티 래퍼를 이용해야 한다

여러 뷰가 접근해야하는 데이터일 경우 Environment 프로터티 래퍼를 이용한다 자식 뷰도 접근할 수 있게 할려면 .environment() 수정자를 추가한다

This post is licensed under CC BY 4.0 by the author.