현재 존재하는 SwiftUI 에서 데이터 전달하는 객체
SwiftUI 에서 Struct 내의 값을 변경할 수 있게 해줍니다.
지속적으로 변형 가능한 변수를 만들 수 있게 해줍니다.
@State는 String, Int, Bool 과 같은 타입에만 사용되는 것을 권장합니다.
일반적으로, @State 변수는 private로 선언되고, 다른 View와 공유되지 않습니다.
다른 View와 값을 공유하고 싶다면, @Binding 또는 @ObservedObject를 사용하시면 됩니다.
사용 예시
struct ContentView: View {
@State private var number = 0
}
@Binding은 부모 View의 @State 와 같은 값을 양방향으로 연결되게 해줍니다.
isPresented 는 showAddView 를 바인딩 시켜줘서 값을 변경해줍니다.
사용 예시
// 부모 View
struct ContentView: View {
// 변경 가능한 변수
@State private var showAddView = false
var body: some View {
VStack {
Button("Trigger") {
showAddView = true
}
}
.sheet(isPresented: $showAddView) {
AddView(isPresented: self.$showAddView)
}
}
}
// 자식 View
struct AddView: View {
// ContentView의 showAddView와 연결된 변수
@Binding var isPresented: Bool
var body: some View {
Button("Dismiss") {
// ContentView의 showAddView를 False로 변경합니다.
self.isPresented = false
}
}
}
Protocol으로 Combine 프레임워크의 일부입니다.
ObservableObject를 사용하기 위해서는, Protocol을 준수하고 @Published를 사용하면 됩니다.
@Published를 사용하면 변수의 값이 추가되거나, 삭제되는 것을 View가 감지할 수 있게 됩니다.
ObservableObject는 MVVM 아키텍쳐의 ViewModel에 적용하기 좋은 프로토콜입니다.
사용 예시
class MyViewModel: ObservableObject {
// ObservableObject 프로토콜을 준수하기 위해 @Published 변수 생성
@Published var dataSource: MyModel
init(dataSource: MyModel) {
self.dataSource = dataSource
}
}
@ObservedObject 와 비슷한 방식으로 작동합니다.
SwiftUI가 View 를 다시 랜더링 할 때, 실수로 취소되는 것을 방지할 수 있습니다.
@StateObject를 통해서 관찰되고 있는 객체는 화면 구조가 재생성되어도 해제되지 않습니다.(아래에서 다시 설명하겠습니다.)
사용 예시
struct ContentView: View {
@StateObject var user = User()
}
class User: ObservableObject {
// ObservableObject 를 준수하기 위해 @Published 변수 생성
@Published var name = "Hohyeon Moon"
}
struct ContentView: View {
// ObservableObject 객체(= User) 를 관찰
@ObservedObject var user = User()
var body: some View {
VStack {
Text("Your name is \(user.name).")
}
}
}
// MySettings.swift
class Settings: ObservableObject {
@Published var version = 0
}
// SceneDelegate.swift
// EnvironmentObject 상위뷰에서 사용하는 방법 - 1
var settings = UserSettings()
window.rootViewController = UIHostingController(rootView: ContentView().environmentObject(settings))
// EnvironmentObject 상위뷰에서 사용하는 방법 - 2
@main
struct MyApp: App {
let settings = AppSettings()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(settings) // 설정 데이터 전달
}
}
}
// MyViews.swift
struct ContentView: View {
// EnvironmentObject 변수를 생성
@EnvironmentObject var settings: UserSettings
var body: some View {
NavigationView {
VStack {
Button(action: {
self.settings.version += 1
}) {
Text("Increase version")
}
NavigationLink(destination: DetailView()) {
Text("Show Detail View")
}
}
}
}
}
// DetailView.swift - ContentView의 자식뷰
struct DetailView: View {
// EnvironmentObject 변수를 생성
@EnvironmentObject var settings: UserSettings
var body: some View {
Text("Version: \(settings.version)")
}
}
Xcode 15 와 Swift 5.9 이후 SwftUI 의 데이터를 사용하는 방식이 변경되었습니다.
// BEFORE
// ObservableObject 프로토콜을 사용한 방식
class Library: ObservableObject {
@Published var books: [Book] = [Book(), Book(), Book()]
}
// AFTER
// Observable Macro를 사용한 방식
@Observable class Library {
var books: [Book] = [Book(), Book(), Book()]
}
// BEFORE
@main
struct BookReaderApp: App {
// @StateObject 사용
@StateObject private var library = Library()
var body: some Scene {
WindowGroup {
LibraryView()
.environmentObject(library)
}
}
}
// AFTER
@main
struct BookReaderApp: App {
// @StateObject -> @State를 사용하면 됩니다.(적응하는 데, 조금 걸릴수도?)
@State private var library = Library()
var body: some Scene {
WindowGroup {
LibraryView()
.environment(library)
}
}
}
// BEFORE
struct LibraryView: View {
// 기존에는 @EnvironmentObject를 사용했습니다.
@EnvironmentObject var library: Library
var body: some View {
List(library.books) { book in
BookView(book: book)
}
}
}
// AFTER
struct LibraryView: View {
// @Environment(Model) 로 바뀌었습니다.
@Environment(Library.self) private var library
var body: some View {
List(library.books) { book in
BookView(book: book)
}
}
}
// BEFORE
struct BookView: View {
// 기존에는 Observable 프로토콜을 채택한 class 를 사용하기 위해서는 @ObservedObject를 사용해야 했습니다.
@ObservedObject var book: Book
var body: some View {
Text(book.title)
}
}
// AFTER
struct BookView: View {
// 이제는 그냥 변수처럼 사용하면 됩니다.
var book: Book
var body: some View {
Text(book.title)
}
}
// BEFORE
struct BookEditView: View {
// 기존
@ObservedObject var book: Book
var body: some View {
TextField("Title", text: $book.title)
}
}
// AFTER
struct BookEditView: View {
// Binding 이 필요한 경우 @Bindable 을 사용하면 됩니다.
// Bindable 을 사용함으로써 Book 객체에 대하여 CRUD가 가능해집니다.
@Bindable var book: Book
var body: some View {
// 구조체 내부 값에 접근하기 위해서는 $을 붙여야 합니다.
TextField("Title", text: $book.title)
}
}
.onAppear {
// 뷰가 나타날 때
print("View appeared")
}
.onDisappear {
// 뷰가 사라질 때
print("View disappeared")
}
@StateObject : ObservedObject 를 생성하는 화면에서도 일관된 결과를 원할 경우 사용
@ObservedObject : 의존 관계로 주입할 때 사용