iOS 🍎/SwiftUI

@State, @Binding , @ObservedObject, @Published, @StateObject, @EnvironmentObject

yongmin.Lee 2022. 9. 13. 17:17

@State

  • SwiftUI는 state로 선언한 모든 프로퍼티의 스토리지를 관리 
  • @State를 앞에 추가하면 SwiftUI가 자동으로 변경사항을 observe하고 해당 state를 사용하는 view부분을 업데이트
  • view 인스턴스를 만들면서 해당 view의 state 프로퍼티는 초기화하면 프로퍼티를 관리하는 swiftui와 충돌이 발생할수 있으므로, 항상 state프로퍼티를 private으로 선언


@Binding

// 부모 view
struct PlayerView: View {
    var episode: Episode
    @State private var isPlaying: Bool = false

    var body: some View {
        VStack {
            Text(episode.title)
                .foregroundStyle(isPlaying ? .primary : .secondary)
            PlayButton(isPlaying: $isPlaying) // Pass a binding.
        }
    }
}

// 자식 view
struct PlayButton: View {
    @Binding var isPlaying: Bool

    var body: some View {
        Button(isPlaying ? "Pause" : "Play") {
            isPlaying.toggle()
        }
    }
}
  • 부모 view의 state 프로퍼티를 자식 view와 양방향연결(read,write)을 할때 사용
  • 자식 view는 @State대신 @Binding으로 받으며, 부모 view는 프로퍼티앞에 $을 붙임으로써 바인딩을 전달

 

@ObservedObject

class UserProgress: ObservableObject {
    @Published var score = 0
}

// 부모 view
struct ContentView: View {
    @StateObject var progress = UserProgress()

    var body: some View {
        VStack {
            Text("Your score is \(progress.score)")
            InnerView(progress: progress)
        }
    }
}

// 자식 view
struct InnerView: View {
    @ObservedObject var progress: UserProgress

    var body: some View {
        Button("Increase Score") {
            progress.score += 1
        }
    }
}
  • 여러 프로퍼티나 메소드가 있는 객체 처럼 복잡한 프로퍼티인 경우 @State 대신 @ObservedObject를 사용
  • swiftui는 ObservableObject 프로토콜을 준수하는 객체 내부에 @Published가 붙은 프로퍼티가 변경되면 SwiftUI에 view reload를 트리거
  • ObservableObject 프로토콜을 준수하는 객체의 인스턴스를 생성하기 위해서는 @ObservedObject가 아닌 @StateObject를 사용해야 한다

 

@EnvironmentObject

// ObservableObject
class Info: ObservableObject {
  @Published var age = 10
}

// 
class SceneDelegate {
...
  var info = Info() 
  window.rootViewController = UIHostingController(rootView: ContentView()
  .environmentObject(info))
...
}

// subview 1
struct MainView: View {
  @EnvironmentObject var info: Info

  var body: some View {
    Button(action: {
      self.info.age += 1
    }) {
      Text("Click Me for plus age")
    }
    SubView()
  }
}

// subview 2
struct SubView: View {
  @EnvironmentObject var info: Info

  var body: some View {
    Button(action: {
      self.info.age -= 1
    }) {
      Text("Click Me for minus age")
    }
  }
}
  • 앱의 여러 뷰에서 공용되는 ObservableObject 객체는 @EnvironmentObject를 사용
  • 필요한 곳 어디에서든 데이터를 공유하고 데이터가 변경 될 때 뷰가 자동으로 업데이튼 된 상태로 유지할 수 있다.
  • 전역적 사용을 위해 SceneDelegate에서 contentView.environmentObject(_:) 메서드를 통해 ObservableObject를 하위뷰들에게 전달하고 이를 사용할 뷰들에서 프로퍼티로 @EnvironmentObject 어노테이션을 붙인 변수를 생성

 

 

 

 

 

 

 

 

 

참고

https://www.hackingwithswift.com/quick-start/swiftui/how-to-use-environmentobject-to-share-data-between-views

https://seons-dev.tistory.com/35

https://developer.apple.com/documentation/swiftui/binding

https://developer.apple.com/documentation/swiftui/state

https://green1229.tistory.com/229

https://zeddios.tistory.com/964?category=796110