Viewの遷移時にアニメーションする(続)

はじめに

Viewの遷移時にアニメーションするにて画面遷移の実践をしてみました。
この方法について、見た目上しっくりと来ない部分があったので、対応策を残しておきたいと思います。

現象

状況の説明

アプリ起動時に画面Aを表示し、画面Aでの処理に応じて画面Bに切り替えるケースで考えていきます。切替時には画面Aをアニメーションで閉じています。

起動してからのアプリの流れは、

  • 画面Aの描画
  • 画面Bへの遷移可否判定
  • 画面Bの描画と画面Aのアニメーションによる消去

このとき期待していた動きは、画面Aの裏側に画面Bを表示しておいて、画面Aをアニメーションで消すに従って画面Bが現れるというものです。ですが、実際は画面Bは画面Aの前面に表示されてしまい、画面Aがそもそも見えない状況になりました。これではアニメーションの意味がありません。

画面Bの背景色が透明で内包するViewもあまりない状態ならばこれでも構いません。ですが、そのようなパターンはあまりないように思います。

コード例

コードを必要最低限で抜き出してみました。画面AとBを呼び出している概念的なコード例はこのような感じになります。

var body: some View {
    ZStack {
        if 画面Aでの処理後、画面Bへの遷移OK {
            画面B
        }
        else {
            画面A
        }
    }
}

画面Aのコード例は、

var body: some View {
    return GeometryReader { geometory in
        VStack (alignment: .leading, spacing: 12.0) {
            諸々のView(ここで画面Bへの遷移可能かどうかを判定して戻している)
        }
        .padding(8.0)
        .frame(width: geometory.size.width, height: geometory.size.height)
        .background(Color.white)
        .animation(.easeInOut(duration: 0.4))
    }
    .transition(.move(edge: .bottom))
}

画面Bのコード例は、

var body: some View {
    return VStack (alignment: .leading, spacing: 12.0) {
          諸々のView
        }
        .padding(8.0)
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .background(Color.orange)
    }

対応方法

試行錯誤の結果、解決策を見つけました。描画される順序を確定させておく方法です。

var body: some View {
    ZStack {
        if 画面Aでの処理後、画面Bへの遷移OK {
            画面B
        }
        else {
            画面A
            .zindex(9) //インデックスの値は1以上の任意の値
        }
    }
}

画面Bへの遷移OKとなった時点で、画面Bの描画がなされます。なされると同時に画面Aを閉じるアニメーションも実行されます。このときの順序は画面Aが画面Bの下に存在する状態です。ならば、ハナから「画面Aは画面Bよりも前面ですよ」と指定しておけばよいのです。

上記コード中のzindex(9)がミソです。何も指定しないときは0が設定されています。1より大きい値を指定した順でViewが前面から表示されます。

まとめ

以上、フラグによる画面遷移をする場合の、Viewの重なり順序による見た目の改善方法でした。(アニメーションの時間をぐっと短くすれば気にはならないんですがね)

ちなみに、画面Bから画面Aへの遷移もできますが、上記のコードだと画面Bが消え去って画面Aのアニメーションが開始されます。これはこれで頂けないので、画面Bを表示(かつ操作不可)した状態で画面Aをアニメーション表示する方法も検討しないといけません。