Sharing Data over Views
Published by @SoNiceInfo at 6/24/2020
Use variables to share data with multiple Views.
Update
@StateObject
was introduced in WWDC20 for iOS 14.@ObservedObject
instance is recreated with view's lifecycle.@StateObject
instance is kept regardless of the state of view.
Introduction
There are three ways to use Property Wrappers.
@Binding
the@State
Variables.- Pass
@ObservableObject
for Each View. - Use
@EnvironmentObject
.
@Binding
and @Published
are used to notify changes of the data to all views.@Binding
the @State
Variables.
Declare the @Binding
variable in the child view, pass it frome the parent view with Binding
.
//
// ContentView.swift
//
import SwiftUI
struct ContentView: View {
@State var name: String = ""
var body: some View {
VStack {
Text("ContentView: ") + Text(name)
InputView(name: $name)
}
}
}
struct InputView: View {
@Binding var name: String
var body: some View {
VStack {
TextField("Placeholder", text: $name)
.padding()
.border(Color.green, width: CGFloat(2))
ResultView(name: $name)
}
}
}
struct ResultView: View {
@Binding var name: String
var body: some View {
HStack {
Text("ResultView: ")
TextField("Placeholder", text: $name)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Pass @ObservableObject
for Each View.
Define the Model as ObservableObject
.
Create an instance of @ObservedObject
in the parent View.
If you declare @ObservedObject
variable in a child View, pass it as an argument to the View.
//
// ContentView.swift
//
import SwiftUI
class ViewModel: ObservableObject {
@Published var name = ""
}
struct ContentView: View {
@ObservedObject var vm = ViewModel()
var body: some View {
VStack {
Text("ContentView: ") + Text(vm.name)
InputView(vm: vm)
}
}
}
struct InputView: View {
@ObservedObject var vm: ViewModel
var body: some View {
VStack {
TextField("Placeholder", text: $vm.name)
.padding()
.border(Color.green, width: CGFloat(2))
ResultView(vm: vm)
}
}
}
struct ResultView: View {
@ObservedObject var vm: ViewModel
var body: some View {
HStack {
Text("ResultView: ")
TextField("Placeholder", text: $vm.name)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Use @EnvironmentObject
.
If you want to use variables on all views, use @EnvironmentObject
.
The point is stating that you are using an environmentObject in SceneDelegate.swift
. With iOS 14, stating in WindowGroup
.
iOS 13 Support
//
// SceneDelegate.swift
//
import UIKit
import SwiftUI
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Create the SwiftUI view that provides the window contents.
// 大事!!
let contentView = ContentView().environmentObject(ViewModel())
...
}
}
iOS 14 Support
import SwiftUI
@main
struct iOS14App: App {
var body: some Scene {
WindowGroup {
ContentView().environmentObject(ViewModel())
}
}
}
Common
//
// ContentView.swift
//
import SwiftUI
class ViewModel: ObservableObject {
@Published var name = ""
}
struct ContentView: View {
@EnvironmentObject var vm : ViewModel
var body: some View {
VStack {
Text("ContentView: ") + Text(vm.name)
InputView()
}
}
}
struct InputView: View {
@EnvironmentObject var vm : ViewModel
var body: some View {
VStack {
TextField("Placeholder", text: $vm.name)
.padding()
.border(Color.green, width: CGFloat(2))
ResultView()
}
}
}
struct ResultView: View {
@EnvironmentObject var vm : ViewModel
var body: some View {
HStack {
Text("ResultView: ")
TextField("Placeholder", text: $vm.name)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}