감자주먹밥

[SwiftUI] @State, @Binding, @ObservedObject, @StateObject 본문

IOS/SwiftUI

[SwiftUI] @State, @Binding, @ObservedObject, @StateObject

JustHm 2023. 1. 14. 18:19
728x90

@State

  • 화면상태를 나타내거나 표시할때 사용되는 타입
  • String, Int, Bool과 같은 간단한 값을 저장하고 View의 현재 상태를 표시하기 위해 사용
  • 화면 내부에서 사용되기 때문에 private를 꼭 붙이는 것을 권장한다.
  • @State변수의 값이 변경되면 뷰는 빠르게 리로드해 변경된 값을 적용한 뷰를 보여준다.
struct Sample: View {
    @State private var text = ""
    @State private var isDisable = true
    var body: some View {
        VStack(alignment: .leading) {
            Text(text)
            Divider()
            TextField("Place holder", text: $text)
            
            Toggle(isOn: $isDisable) {
                Text("버튼 비활성화 하기")
            }
            Button("버튼", action: {})
                .disabled(isDisable)
        }
        .padding()
    }
}

 

@Binding

다른 값을 바인딩 하는 Property Wrapper타입 

  • ChildView에서 ParentView의 값을 표시하고, 능동적으로 값이 변화할 때 사용. (ChildView에서 사용됨)
  • 매개변수로 넘겨줄 때 $표시를 사용한다. 상호간 get/set이 가능하게 하기 위해서.
struct ParentView: View {
    @State private var isDisable = true
    var body: some View {
        VStack {
            Sample(isDisable: $isDisable)
        }
    }
}
//ChildView
struct Sample: View {
    @Binding var isDisable: Bool
    var body: some View {
        VStack(alignment: .leading) {
            Toggle(isOn: $isDisable) {
                Text("버튼 비활성화 하기")
            }
            Button("버튼", action: {})
                .disabled(isDisable)
        }
        .padding()
    }
}

실행 결과는 @State에서 했던것과 같다. 상위뷰에서 값을 가지고 있고 하위뷰는 값을 바인딩을 통해 참조만 해서 사용하는 형태다.

@ObservableObject, @Published, @ObservedObject

  • Observable Object 독립적인 오브젝트를 만들고 그 안에 상태를 저장할 수 있다.
  • ObservableObject는 class bound 프로토콜, class로만 생성가능
  • @Published를 사용해 관찰 가능한 상태를 만들 수 있다. 값이 변경되면 view reload.
  • @ObservedObject를 사용해 ObservableObject의 인스턴스를 만들고 관찰할 수 있다.
  • 관찰중인 데이터가 변경되면 화면을 다시 그린다.
  • 값이 변경되거나 이벤트가 생기면 단일 뷰에만 변경이 일어나고 똑같은 인스턴스를 가지고 있는 다른뷰에는 변경이 일어나지 않는다.
class Model: ObservableObject { //관찰 가능한!
    @Published var isDisabled = true
}

struct ParentView: View {
    @ObservedObject var model = Model()
    var body: some View {
        VStack {
            Sample(isDisable: $model.isDisabled)
        }
    }
}

실행 결과는 다 동일하다.

@EnvironmentObject 

  • EnviromentObject는 별도로 값을 전달해주지 않아도 상속받는 부모로부터 함께 적용되는 오브젝트
  • 모든 뷰에서 접근 할 수 있는 모델을 생성하는 방법, (Singleton?)
  • .environmentObject(:) 매서드를 상위 뷰에 호출해 모델 객체를 설정한다.

SwiftUI에서 Singleton을 직접 구현하는 것 보다 EnvironmentObject를 사용해서 공유하는게 더 적절할 거 같다.

struct App {
    var body: some Scene {
        WindowGroup {
            ContentView().environmentObject(sharedViewModel)
        }
    }
}    
struct ContentView: View {
    var body: some View {
		ChildView()
    }
}

struct ChildView: View {
    @EnvironmentObject private var sharedVm: SharedViewModel
    var body: some View {
		/*ViewLogic...   */
    }
}

상위뷰에 .environmentObject(:)로 설정해놓으면 연결되는 하위뷰들은 전부 @EnvironmentObject를 통해 모델객체를 사용할 수 있다.

Preview를 사용할 때도 PreviewProvider 내부 뷰에도 .environmentObject(모델객체)를 해줘야 한다.

@StateObject

위 PropertyWrappers와 다르게 지원 버전이 IOS14다.

ObservedObject와 같은 동작을하지만 차이점이 있다.

  • ObservableObject는 View의 LifeCycle에 의존해 View가 새로 그려질 때 새로 생성되지만, StateObject는 View LifeCycle에 의존하지 않고, 참조를 가지고 있어 유지된다.
final class CounterViewModel: ObservableObject {
    @Published var count = 0
    func increase() {
        count += 1
    }
}
struct NumberView: View {
    @State var count = 0
    var body: some View {
        VStack {
            Text("First Count: \(count)")
            Button("count") {
                count += 1
            }
            CounterView()
        }.padding(.bottom)
    }
}
struct CounterView: View {
    @ObservedObject var viewModel = CounterViewModel() //이 부분
    var body: some View {
        VStack {
            Text("Second Count: \(viewModel.count)")
            Button("count") {
                viewModel.increase()
            }
        }
    }
}

 

CounterView에 ObservedObject를 사용한 것과 StateObject를 사용한 것의 차이가 있다.

ObservedObject 사용시, NumberView의 버튼을 눌러 값이 변경되고 화면을 새로 그리게 되면, CounterView의 화면도 새로 그리게 되며, ObservableObject역시 새로 생성된다. 결국 count를 올렸던 부분이 0으로 다시 초기화 된다.

StateObject 사용시, NumberView 버튼을 눌러 값이 변결되고 화면이 새로 그려져도 StateObject의 참조는 그대로 남아 있어 값이 유지된다.

728x90
Comments