[SwiftUI]同一画面内のViewの切り替えベストプラクティス
同一画面内でのViewの切り替え
SwiftUI
の場合、同一画面内
でViewを切り替える場面はよくあるかと思います。例えば、データ読み込み中
, データの表示
, エラー表示
の場合などです。
切り替えの方法も、if
やswitch
の方法がありますが、今回はswitch
でできるだけ簡単な方法を使います。
使用する手法としては
-
enum
で切り替えパターンを定義し、@State
で管理します - ①コールバックを使う方法、②
Binding
を使う方法の2パターンを使います
それぞれのパターンの実装を見て、メリット・デメリットを考えてみたいと思います。
enumで切り替えパターンを定義
切り替えパターンは以下のように定義します。
enum ContentState {
case first //1つ目のView
case second(text: String) //2つ目のView(値を渡します)
case third //3つ目のView
}
ContentView
メインのViewは以下のように定義します。
struct ContentView: View {
@State var state: ContentState = .first
var body: some View {
NavigationView {
switch state {
case .first:
FirstView { text in
state = .second(text: text)
}
case .second(let text):
SecondView(text: text) {
state = .third
}
case .third:
ThirdView(state: $state)
}
}
}
}
先ほど定義したContentState
を使い、初期状態を@State
を使って定義します。
switch
を使い、それぞれのパターンでViewをだし分けしています。
FirstView
, SecondView
については、①コールバックを使う方法、ThirdView
については②Binding
を使う方法を使用しています。
FirstView -> SecondView -> ThirdView -> FirstViewのような切り替えの流れにしています。
FirstView
, SecondView
, ThirdView
は以下のような実装です。
struct FirstView: View {
var onClick: (String) -> Void
var body: some View {
VStack(spacing: 32) {
Text("First")
.font(.title)
Button("Next") {
onClick("From first")
}
}
}
}
struct SecondView: View {
var text: String
var onClick: () -> Void
var body: some View {
VStack(spacing: 32) {
Text("Second")
.font(.title)
Text(text)
Button("Next") {
onClick()
}
}
}
}
struct ThirdView: View {
@Binding var state: ContentState
var body: some View {
VStack(spacing: 32) {
Text("Third View")
Button("go to First") {
state = .first
}
}
}
}
①コールバックを使う方法
メリット
- サブView側でenumの中身を知らなくても良いので、コードが追いやすい
- サブViewの切り替えが一方向になりやすい(1つのサブViewに1つのCallbackの場合)
デメリット
- 切り替え先が複数ある時は使いにくい
- コードが少し増える
②Bindingを使う方法
メリット
- コールバックがないので、親View側のコードが簡単になる
- サブViewの切り替えが複数にまたがることができる
デメリット
- サブViewの中でenumの中身を知らなくてはならない
- サブViewの切り替え箇所が追いにくくなる
終わりに
コールバックを使う方法とBindingを使う方法を比べてみました。2つとも便利な方法ですがデメリットもあります。
実装する際、このメリット・デメリットを考慮して適切な方を選ぶようにできれば、読みやすくメンテナンス性が高いコードが書けると考えられます。
補足
今回、Viewの切り替えの管理にObservableObject
を使いませんでした。ObservableObject
はMVVM
アーキテクチャのViewModel
に当たりますが、Viewの切り替えロジックはView
側に持った方が良いと考えたからです。そうすることでViewModelが複雑にならないようにした意図があります。
サンプルコード
Discussion