データを複数のViewで共有する
Published by @SoNiceInfo at 6/24/2020
データを複数のViewで共有するには変数を使います。
更新情報
iOS 14で@StateObjectが発表されました。@ObservedObjectのインスタンスはViewが描画されるたびに再生成されます。@StateObjectのインスタンスはViewの状態に関わらず維持されるという特徴があります。
導入
変数を使うには以下の3つの方法があります。
@State変数を@Bindingする@ObservableObjectをView毎に渡す@EnvironmentObjectを利用する
@Binding, @Publishedを使うことでデータの変更はすべてのViewに反映されます。@State変数を@Bindingする
変数を使いたい子Viewの中で@Bindingで宣言し、子Viewを呼び出すときに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()
}
}
@ObservedObjectをView毎に渡す
ObservableObjectとしてModelを定義します。
親Viewで@ObservedObjectとしてインスタンスを作成します。
子Viewで@ObservedObjectとして宣言した場合には、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()
}
}
@EnvironmentObjectを利用する
変数をすべてのViewで使いたいときには@EnvironmentObjectを利用します。
ポイントはSceneDelegate.swiftにenvironmentObjectを使うことを明示することです。 iOS14の場合WindowGroupに明示します。
iOS 13対応
//
// 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対応
import SwiftUI
@main
struct iOS14App: App {
var body: some Scene {
WindowGroup {
ContentView().environmentObject(ViewModel())
}
}
}共通
//
// 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()
}
}

