SwiftUI Property Wrapper
2025. 1. 30. 16:40ㆍswift
- @State
- @Bingding
- @StateObject
- @ObservedObject
- @EnvironmentObject
- @Environment
@State
- SwiftUI View 내부에서 사용되는 간단한 값 타입(Bool, String, Int 등)을 저장하고, 그 값이 변경될 때마다 뷰가 재렌더링되도록 하는 속성 래퍼.
- 뷰 전체의 “상태(state)”라고 생각할 수 있으며, 해당 뷰에서만 유효합니다.
- @State는 구조체 기반의 View에서 내부적으로 값을 감싸고 있습니다.
- 값이 변경되면 자동으로 View가 새롭게 그려집니다.
- 다른뷰에서는 관찰할 필요가 없는 경우에 사용합니다.
struct MyView: View {
@State private var isSheetPresented: Bool = false
var body: some View {
VStack {
Text("Hello, SwiftUI!")
.onTapGesture {
isSheetPresented = true
}
}
.sheet(isPresented: $isSheetPresented) {
Text("Sheet View")
}
}
}
@Binding
- 부모View가 소유한 상태(@State, 등)를 자식View에 참조 형태로 전달하기 위해 사용합니다.
- 자식View에서 직접 상태를 소유하지않고, 부모View의 상태를 Binding 받아 동기화합니다.
- 실제 값은 부모에 있고, 자식은 그 값에 대한 포인터를 갖습니다.
- 자식 뷰에서 바인딩된 값을 변경하면, 부모의 상태도 자동으로 변경되어 상위 뷰가 업데이트 되고, 그에 따라 자식도 다시 랜더링 됩니다.
struct ParentView: View {
@State private var count: Int = 0
var body: some View {
ChildView(value: $count) // 자식 뷰로 바인딩 전달
}
}
struct ChildView: View {
@Binding var value: Int
var body: some View {
VStack {
Text("Current value: \(value)")
Button(action: {
value += 1 // 부모 뷰의 count가 증가
}) {
Text("Increment")
}
}
}
}
@StateObject
- 클래스타입(ObservableOnject)을 뷰 내부에서 생성하고, SwiftUI가 해당 객체를 소유하며 라이프사이클을 관리할 수 있도록하는 래퍼
뷰가 다시 그려져도 객체 자체는 다시 생성하지 않고 유지
됩니다!ObservableObject
프로토콜을 채택한 클래스를 View가 직접 소유할 때에 사용합니다(뷰모델의 경우!)- @StateObject 로 선언된 객체 @Published 프로퍼티가 바뀌면, 해당 객체를 구독(subscribe) 중인 뷰들이 자동으로 업데이트됩니다.
- SwiftUI 초기 랜더링 시 객체를 초기화하고, 재렌더링 시에는 재사용합니다.
@ObservaedObject
와 달리, 이 뷰 내부에서 새롭게 생성된 @ObservableObject를한번만
생성을 보장
class Counter: ObservableObject {
@Published var value: Int = 0
}
struct CounterView: View {
@StateObject private var counter = Counter()
var body: some View {
VStack {
Text("Count: \(counter.value)")
Button("Increment") {
counter.value += 1
}
}
}
}
@ObservedObject
ObservableObject
를 관찰하기 위한 래퍼이지만서도, 뷰 외부에서 전달된 객체를 추적할때에 사용합니다.@Published
프로퍼티가 변경되면, 해당 뷰가 자동으로 업데이트 됩니다.- 즉, 부모 뷰(ParentView)가 @StateObject로 객체를 소유하고 있고, 자식 뷰(ChildView)는 @ObservedObject를 통해 이를 관찰하는 구조입니다.
- ObservableObject 내부의 @Published 프로퍼티가 변경되면, 이를 관찰하는 모든 뷰(@ObservedObject를 가진 뷰)에서 다시 렌더링이 발생합니다.
- 뷰가 새로 만들어질때 마다
@ObservedObject
로 받는 객체가 새로 교체될 수 있습니다. - 뷰가 직접 생성하고 소유하는 것이 아니라,
보통 상위에서 주입
받는 경우가 많습니다. - @StateObject와 달리,
뷰가 소유
하는 개념이 없으므로, 라이프사이클이 보장되지 않습니다.
class TimerModel: ObservableObject {
@Published var time: Date = Date()
// 타이머 동작 로직
}
struct ParentView: View {
@StateObject private var timerModel = TimerModel()
var body: some View {
ChildView(timerModel: timerModel)
}
}
struct ChildView: View {
@ObservedObject var timerModel: TimerModel
var body: some View {
Text("Current time: \(timerModel.time)")
}
}
- ParentView는 timerModel을 소유 (@StateObject)하고, ChildView는 timerModel을 관찰 (@ObservedObject).
- 인스턴스 생성해서 가지고 쓰지만 관찰하는입장에서느 변경될때에 뷰가 변경.
ObservedObject정리
- ParentView가 @StateObject를 통해 TimerModel을 직접 소유
- ChildView가 @ObservedObject로 TimerModel을 관찰
- @Published 프로퍼티(time)가 변경될 때마다 관찰하는 뷰(ChildView)가 다시 렌더링됨
- 하지만 ChildView가 재생성되면 timerModel 인스턴스는 유지됨 (부모가 소유하고 있기 때문)
@StateObject vs @ObservedObject 차이점 정리
속성 래퍼 | 역할 | 사용 예시 | 라이프사이클 |
@StateObject | 뷰가 직접 소유하는 ObservableObject | 부모 뷰가 ObservableObject를 관리해야 할 때 | 뷰가 처음 생성될 때 한 번만 인스턴스화됨 |
@ObservedObject | 외부에서 주입받아 관찰하는 ObservableObject | 부모로부터 ObservableObject를 받아 사용하는 자식 뷰 | 뷰가 새로 그려질 때마다 인스턴스 변경 가능 |
@EnvironmentObject
- SwiftUI의 Environment 에
ObservableObject
를 주입하면, 해당뷰 트리 전체에서 공유
할 수 있다. - 자식 뷰 어디서든
@EnvironmentObject
로 같은 객체를 자동 DI받게 됩니다. - APP 전체에서 공통으로 필요한 데이터나 설정을 저장하고, 여러 하위 뷰가 이를 공유할 때 유용합니다.
@EvironmentObject
도 내부적으로는ObservableObject
를 구독하고 있으므로, 해당 객체의 @Published 프로퍼티가 변경되면 UI가 자동 업데이트 됩니다.- 선언부에서 타입만 지정하면 되고, 실제 인스턴스는 최상위뷰에서 .environmentObject(??)로 주입애햐 합니다.(environmentObject(viewModel))
class UserSettings: ObservableObject {
@Published var username: String = "Guest"
}
@main
struct MyApp: App {
@StateObject private var settings = UserSettings()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(settings) 여기서 주입
}
}
}
struct ContentView: View {
var body: some View {
ProfileView()
}
}
struct ProfileView: View {
@EnvironmentObject var settings: UserSettings
var body: some View {
Text("Hello, \(settings.username)")
}
}
- ProfileView에서 @EnvironmentObject var settings만 선언하면, MyApp에서 주입된 UserSettings 인스턴스를 자동으로 참조하게 됩니다.
@Environment
- SwiftUI가 공급하는 기본환경값을 읽기 위해 사용하는 속성래퍼.(색상, 레이아웃방향, 디스플레이크기)
- 키를 통해 시스템이 제공하거나 커스텀한 값에 접근할 수 있습니다.
@Environment
와 달리객체가 아닌 단일 환경 값
에서 가져옵니다.- @Environment(.\colorsScheme)처럼 EnvironmentKey를 통해 값을 가져올 수 있습니다.
struct MyView: View {
@Environment(\.colorScheme) var colorScheme
var body: some View {
Text("Current color scheme: \(colorScheme == .dark ? "Dark" : "Light")")
}
}
간단하게 환경에서 현재 다크모드 여부를 가져와 UI에서 적용할 수 있습니다.
간단한 요약
@State: 뷰 내부에서 간단한 상태(값 타입)를 소유. 값이 바뀌면 해당 뷰 재렌더링.
@Binding: 부모의 @State나 다른 상태 객체를 참조하여, 자식 뷰에서 읽고 쓸 수 있게 함.
@StateObject: 뷰가 직접 소유하는 ObservableObject. 뷰의 라이프사이클에 맞춰 한 번만 생성되며, @Published 변경 시 뷰 업데이트.
@ObservedObject: 외부(부모 등)에서 전달되는 ObservableObject를 관찰. 뷰 자체가 객체를 소유하지 않음.
@EnvironmentObject: 환경(Environment)에 등록된 ObservableObject를 뷰에서 자동 주입받아 사용. 여러 하위 뷰가 공통 객체를 공유할 때 유용.
@Environment: SwiftUI가 제공하는 환경 값(colorScheme, locale 등)을 읽어오는 래퍼.
'swift' 카테고리의 다른 글
가치와 원칙에 대해서 (0) | 2024.04.15 |
---|---|
Swift에서 크래시안나게 하는 습관 (0) | 2024.04.15 |
ARC (2) | 2024.01.08 |
ISP: 인터페이스 분리 원칙 (1) | 2023.12.03 |
LLVM 에 대해서 알아보자. (0) | 2023.09.18 |