SwiftUI @State, @ObservedObject, @EnvironmentObject 익히기
앱을 개발하려고 보면, 사용자의 입력을 받든, 서버에서 값을 가져오든, 변수에 값을 저장하고, 그 값의 변화에 따라 화면의 UI 가 바뀐다. objective-c와 이전의 swift 에서는 어떻게 했는지 까먹었으나, SwiftUI 는 새로 공부해보면서 정리하려고한다.
There are several concepts regarding the binding between data and view of the data in SwiftUI. Below is what I understood from other developer's blogs.
With @State basically, struct View 밖의 special memory 에 저장되고, it is managed by SwiftUI. 이 값이 바뀔 때마다 SwiftUI 는 해당 view 를 rebuild 한다.
다만, view 에서 접근 하기 위해서는 ObservableObject 를 init 할때 매개변수로 전달 해주어야 한다.
But, ObservedObject 와는 달리, EnvinronmentObject can be set at first, in the SceneDelegate class. And in the SceneDelegate, we can create the class and then put in the view with .environmentObject(). By doing this, 모든 child views 들은 이 object 의 변화를 감지할 수 있다.
There are several concepts regarding the binding between data and view of the data in SwiftUI. Below is what I understood from other developer's blogs.
@State
The most basic annotation that we can use. The important thing is it can be only used in a specific view. So usually I'd use with `private` keyword.With @State basically, struct View 밖의 special memory 에 저장되고, it is managed by SwiftUI. 이 값이 바뀔 때마다 SwiftUI 는 해당 view 를 rebuild 한다.
@State private var showFavorited: Bool = false
var body: some View {
VStack{
Button(
action: { self.showFavorited.toggle() },
label: { Text("Change filter") }
)
if !self.showFavorited{
Text("asdf")
}else{
Text("zxcv")
}
}
}
@ObservedObject
This keyword means that the data can be observed in many independent views. In general, class should extends ObservableObject. And the property inside the class can annotated with @Published. Then struct view which uses this class can observe this value and the view will rebuild when change is made.다만, view 에서 접근 하기 위해서는 ObservableObject 를 init 할때 매개변수로 전달 해주어야 한다.
let pod = PodcastPlayer();
let contentView = ContentView(player:pod)
final class PodcastPlayer: ObservableObject {
@Published private(set) var isPlaying: Bool = false
func play() {
isPlaying = true
}
func pause() {
isPlaying = false
}
}
struct ContentView: View {
@ObservedObject var player: PodcastPlayer
var body: some View {
List {
Button(
action: {
if self.player.isPlaying {
self.player.pause()
} else {
self.player.play()
}
}, label: {
Text(player.isPlaying ? "you can pause": "you can play")
}
)
}
}
}
@EnvirnmentObject
The class also should inherit from ObservableObject. And It should have some variables with @Published annotation that will be observed.But, ObservedObject 와는 달리, EnvinronmentObject can be set at first, in the SceneDelegate class. And in the SceneDelegate, we can create the class and then put in the view with .environmentObject(). By doing this, 모든 child views 들은 이 object 의 변화를 감지할 수 있다.
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
let store = ReposStore(service: .init())
let contentView = ContentView().environmentObject(store)
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView)
self.window = window
window.makeKeyAndVisible()
}
}
class ReposStore: ObservableObject{
@Published private(set) var repos: [Repo] = []
private let service: GithubService
init(service: GithubService) {
self.service = service
}
func fetch(matching query:String){
service.search(matching: query){[weak self] result in
DispatchQueue.main.async {
switch result {
case .success(let repos): self?.repos = repos
case .failure: self?.repos = []
}
}
}
}
}
struct ContentView: View {
@State private var query: String = "Swift"
@EnvironmentObject var repoStore: ReposStore
var body: some View {
NavigationView{
List{
TextField("Type someting...", text: $query, onCommit: fetch)
ForEach(repoStore.repos){ repo in
RepoRow(repo: repo)
}
}.navigationBarTitle(Text("Search"))
}.onAppear(perform: fetch)
}
private func fetch() {
repoStore.fetch(matching: query)
}
}
Conclusion
간단하게 이해한 바대로 정리하자면, state 는 그 상태가 바뀔 때마다 view 를 다시 그린다. ObservableObject 를 implement 한 class 는 Published 변수를 가질 수 있고, published 값들에 관심이 있는 각 객체들은 이것을 observe 하면서 값이 변할 때마다 화면을 다시 그리는데, environment 단에서 detect 가 가능하면 @EnvironmentObject 가 되고, init 을 통해 접근 가능한 여러 view 들에서 접근하면 @ObservedObject 가 된다. Observable 한 객체가 Published 한 것을 envObj 나 observedObj 를 통해 관찰하고, 변경시 UI 역시 다시 그리는 것이다.
@Binding 이라는 것도 있는데, 이것은 @State 객체를 child View 에게 reference 하도록 넘김으로써, child view 가 이 값을 read, write 할 수 있게 한다. 하지만 그렇다고 해서 child view 가 이값의 변화를 observe 할 수 있는것은 아니다.
Comments
Post a Comment