SwiftUI는 상태 프로퍼티, Observable 객체, Environment 객체를 제공하며, 이들 모두는 사용자 인터페이스의 모양과 동작을 결정하는 상태를 제공한다. SwiftUI에서 사용자 인터페이스 레이아웃을 구성하는 뷰는 코드 내에서 직접 업데이트하지 않는다. 그 대신, 뷰와 바인딩된 상태 객체가 시간이 지남에 따라 변하면 그 상태에 따라 자동으로 뷰가 업데이트된다.
상태 프로퍼티는 상태에 대한 가장 기본적인 형태이며, 뷰 레이아웃의 현재 상태를 저장하기 위해서만 사용된다. @State 프로퍼티 래퍼를 사용하여 선언된다.
struct ContentView: View {
@State private var wifiEnabled = true
@State private var userName = ""
var body: some View {
.
.
상태 값은 해당 뷰에 속한 것이기 때문에 private 프로퍼티로 선언되어야 한다.
상태 프로퍼티에 의존하는 모든 뷰는 최신 값이 반영되도록 업데이트된다.
상태 프로퍼티를 선언했다면 레이아웃에 있는 뷰와 바인딩을 할 수 있다. 바인딩이 되어 있는 뷰에서 변경이 일어나면 해당 상태 프로퍼티에 자동으로 반영된다.
상태 프로퍼티와의 바인딩은 프로퍼티 이름 앞에 '$'을 붙이면 된다.
struct ContentView: View {
@State private var wifiEnabled = true
@State private var userName = ""
var body: some VIew {
VStack {
Toggle(isOn: $wifiEnabled) {
Text("Enable Wi-Fi")
}
TextField("Enter user name", text: $userName)
Text(userName)
Image(systemName: wifiEnabled ? "wifi" : "wifi.slash")
}
}
}
struct ContentView: View {
@State private var wifiEnabled = true
@State private var userName = ""
var body: some VIew {
VStack {
Toggle(isOn: $wifiEnabled) {
Text("Enable Wi-Fi")
}
TextField("Enter user name", text: $userName)
Text(userName)
WifiImageView(wifiEnabled: $wifiEnabled)
}
}
}
struct WifiImageView: View {
@Binding var wifiEnabled : Bool
var body: some View {
Image(systemName: wifiEnabled ? "wifi" : "wifi.slash")
}
}
어떤 뷰가 하나 이상의 하위 뷰를 가지고 있으며 동일한 상태 프로퍼티에 대해 접근해야 하는 경우 @Binding 프로퍼티 래퍼를 이용하면 해결된다.
Observable 객체는 여러 다른 뷰들이 외부에서 접근할 수 있는 영구적인 데이터를 표현하기 위해 사용된다. 일반적으로 시간에 따라 변경되는 하나 이상의 데이터 값을 모으고 관리하는 역할을 담당한다. 또한, 타이머나 알림과 같은 이벤트를 처리하기 위해 사용될 수도 있다.
Observable 객체는 게시된 프로퍼티로서 데이터 값을 게시한다. 그런 다음, 게시자를 구독하여 게시된 프로퍼티가 변경될 때마다 업데이트를 받는다.
Combine 프레임워크에 포함되어 있는 Observable 객체는 게시자와 구독자 간의 관계를 쉽게 구축할 수 있도록 iOS 13에 도입되었다.
@Published 프로퍼티 래퍼를 사용하면 래퍼 프로퍼티 값이 변경될 때마다 모든 구독자에게 업데이트를 알리게 된다.
import Foundation
import Combine
import SwiftUI
class DemoData : ObservableObject {
@Published var userCount = 0
@Published var currentUser = ""
init() {
// 데이터 초기화
updateData()
}
func updateData() {
// 데이터 최산 상태 유지
}
}
struct ContentView: View {
@ObservedObject var demoData : DemoData
var body: some View {
Text("\(demoData.currentUser), you are user number \(demoData.userCount)")
}
}
struct ContentView_Previews: PreviewProvide {
static var previews: some View {
ContentView(demoData: DemoData())
}
}
어떤 뷰에서 다른뷰로 이동하는데 이동될 뷰에서도 동일한 구독 객체에 접근해야 한다면, 이동할 때 대상 뷰로 구독 객체에 대한 참조체를 전달해야 한다.
@ObservedObject var demoData: DemoData = DemoData()
.
.
NavigationLink(destination: SecondView(demoData)) {
Text("Next Screen")
}
NavigationLink는 SecondView라는 이름의 다른뷰로 이동하기 위해 사용되며, demoData객체에 대한 참조체를 전달한다.
앱 내의 여러 뷰가 동일한 구독 객체에 접근해야 하는 경우에는 Environment 객체를 사용하는 것이 더 합리적일 수 있다.
Environment 객체는 SwiftUI 환경에 저장되며, 뷰에서 뷰로 전달할 필요 없이 모든 뷰가 접근할 수 있다.
Environment 객체를 구독해야 하는 객체는 @EnvironmentObject 프로퍼티 래퍼를 이용하여 해당 객체를 참조하면 된다.
@EnvironmentObject var demoData: DemoData
Environment 객체는 옵저버 내에서 초기화될 수 없으므로 접근하는 뷰가 화면을 설정하는 동안 구성해야 한다. SceneDelegate.swift 파일의 willConnectTo 메서드를 수정해야 한다.
let contentView = ContentView()
let demoData = DemoData()
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView.environmentObject(demoData))
self.window = window
window.makeKeyAndVisible()
}
SwiftUI 프리뷰 캔버스 내에서 Environment 객체를 사용하려면 PreviewProvider 선언부를 다음과 같이 수정해야 한다.
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView().environmentObject(DemoData())
}
}