初めに
スマホ下のタブをポチポチ切り替えるたびに
画面描画に必要なデータを用意するのって非効率だと思いません?
今回は、一度表示させたデータを一時的に保持し、
2回目以降同じ画面を表示させた時に、
保持しといたデータを再利用したいと思います。
寄り道多めですが、ご了承ください m(_ _)m
方法
-
ObservableObject
プロトコルに準拠したclass
を定義する -
@StateObject
を使ってデータを保持する - 保持したデータを使って画面を描画する
ObservableObject
プロトコルに準拠したclass
???
要はクラスのプロパティの変更を検知して、ビューを自動的に更新できます。
使用するには?
ObservableObject
をクラス宣言で指定する- 検知したいプロパティに
Published
を付ける
サンプル
class Counter: ObservableObject {
@Published var count: Int = 0
}
シンプルですね。(^^)
せっかくクラスを定義したので
ボタンを押すとカウントが増える ロジックを書いてみましょう。
import SwiftUI
// ObservableObjectプロトコルに準拠したclass
class Counter: ObservableObject {
@Published var count: Int = 0
}
struct ObservableView: View {
// 定義したクラスを読み込む
@StateObject private var counter = Counter()
var body: some View {
VStack {
Text("Count: \\(counter.count)")
.font(.largeTitle)
.padding()
Button(action: {
counter.count += 1
}) {
Text("Increment")
.font(.headline)
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
}
}
}
}
デモ
1画面で変更を検知するだけならObservableObject
を使う必要もないです。
親のビューで@State
を使ってデータを持っておけばOKです。
しかし、実際のアプリはもっと複雑で、
複数の画面でのデータの持ち方を考えなければなりません。
@ObservedObject
と @StateObject
@StateObject
を調べているとハッピーセットのように@ObservedObject
の記事もヒットします。この両者の違いについて整理したいと思います。
私もここの理解不足で苦しみました _| ̄|○
要は
@ObservedObject
は親ビューのプロパティに更新があるとリセットされるのに対して、@StateObject
は値を保持したままになります。
実際にサンプルを使って見てみましょう。
サンプル
import SwiftUI
// ObservableObjectプロトコルに準拠したclass
class ViewModel: ObservableObject {
@Published var counter = 0
}
struct ParentView: View {
@State var counter = 0
var body: some View {
VStack {
VStack {
Button(action: {
counter += 1
}) {
Text("親ボタン")
.font(.headline)
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
}
Text("親ViewのCount:\(counter)")
}
.padding(.bottom, 10)
// 子を呼び出し
ChildView()
}
}
}
struct ChildView: View {
// 定義したクラスを読み込む
@ObservedObject var observedViewModel = ViewModel()
@StateObject var stateViewModel = ViewModel()
var body: some View {
VStack {
VStack {
Button(action: {
observedViewModel.counter += 1
}) {
Text("@ObservedObjectボタン")
.font(.headline)
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
}
Text("@ObservedObjectのCount:\(observedViewModel.counter)")
}
.padding(.bottom, 10)
VStack {
Button(action: {
stateViewModel.counter += 1
}) {
Text("stateViewModelボタン")
.font(.headline)
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
}
Text("@StateObjectのCount:\(stateViewModel.counter)")
}
}
}
}
デモ
デモの通り @ObservedObject
は親ビューのプロパティに更新があるとリセットされるのに対して、@StateObject
は値を保持できます。
どちらかを採用するかは使いどころによります。
データの保持が必要ない場合は @ObservedObject
を使う。
親ビューが変更されても、子ビューのデータを破棄したくない場合は@StateObject
を使う
といった感じでしょうか。
このデータ保持の仕組みを使って、タブを切り替えてもデータが保持されるように作ってみましょう。
タブを切り替えてみる
タブの生成には TabView
を使用しています。
import SwiftUI
struct ContentView: View {
var body: some View {
TabView{
FirstView()
.tabItem { Text("ファーストビュー") }
SecondView()
.tabItem { Text("セカンドビュー") }
}
}
}
// ObservableObjectプロトコルに準拠したclass
class firstViewModel: ObservableObject {
@Published var counter = 0
}
struct FirstView: View {
@StateObject var stateViewModel = firstViewModel()
var body: some View {
VStack {
Text("ファーストビュー")
Button(action: {
stateViewModel.counter += 1
}) {
Text("カウント")
.font(.headline)
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
}
Text("Count:\(stateViewModel.counter)")
}
}
}
// ObservableObjectプロトコルに準拠したclass
class secondViewModel: ObservableObject {
@Published var counter = 0
}
struct SecondView: View {
@StateObject var stateViewModel = secondViewModel()
var body: some View {
VStack {
Text("セカンドビュー")
Button(action: {
stateViewModel.counter += 1
}) {
Text("カウント")
.font(.headline)
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
}
Text("Count:\(stateViewModel.counter)")
}
}
}
デモ
カウント数がビューを切り替えても保持されている様子がわかります。
今回はシンプルにカウント数を保持するロジックを書いてみましたが、
もっと便利に使えそうな気がします。
- スクロール数を保持
- API通信で一度取得したデータを保持して使い回す
etc…
最後に
ここまで見ていただきありがとうございます。m(_ _)m
@ObservedObject
やら @StateObject
やら@State
といろんな概念が出てくるのでSwift
って大変です。(^^;;
めげずに少しづつ使いこなせるようになりたいです。
参照
【SwiftUI】TabView の selection: Int を使う際の注意点(@ObservedObject と @StateObject の違い)