📚

[SwiftUI]同一画面内のViewの切り替えベストプラクティス

2021/09/06に公開

同一画面内でのViewの切り替え

SwiftUIの場合、同一画面内でViewを切り替える場面はよくあるかと思います。例えば、データ読み込み中, データの表示, エラー表示の場合などです。
切り替えの方法も、ifswitchの方法がありますが、今回は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を使いませんでした。ObservableObjectMVVMアーキテクチャのViewModelに当たりますが、Viewの切り替えロジックはView側に持った方が良いと考えたからです。そうすることでViewModelが複雑にならないようにした意図があります。

サンプルコード

https://github.com/usk-sample/RouterSwitchSample

Discussion