LoginSignup
2
2

【SwiftUI】push遷移先でタブバーを非表示にした後、前画面に戻った時にタブの表示が遅れる問題の解決法

Posted at

概要

以下のような仕様を実現したいと考え、TabViewとNavigationStackを使用することにしました。

  • 複数のタブがある
  • タブの中の画面から別画面にpush遷移するが、その際、表示する画面はフルスクリーンで表示したい
  • 別のタブでも上記を実現したい
    (複数のタブでNavigationStackによる遷移を利用するため、NavigationStackでTabViewをラップすることは出来ない)

この仕様に対して、TabViewでNavigationStackをラップし、.toolbar(, for:)にhiddenを設定すれば良いと考えていましたが、以下の問題が発生しました。

遷移元から別画面にpush遷移後、遷移元に戻るとタブバーの表示にラグが発生する

この問題に対して、以下で考察と解決法の実装を行います。

考察

遷移先の画面(SecondView)に遷移後、前画面 (FirstView)に戻ると問題なくタブは表示されるのですが、上述の通りタブバーの表示にはラグがありました。
.toolbar(, for:)を特に設定しなければ、各Viewのツールバー設定はデフォルトでは.visibleになっているはずなので、Viewが表示されたタイミングでこの設定が適用されており、ここにタブバー表示のラグの原因があると考えました。
そのため、前画面のFirstViewへ戻る前に.hiddenに設定された.toolbar(, for:).visibleに設定し直せば良いのでは?と考えました。

この考えをベースに以下で実装します。

実装

まずはルートの部分を以下のようにTabViewが親となるようにNavigationStackを配置します。

SampleApp.swift
@main
struct NavigationSampleProjectApp: App {
    init() {
        UITabBar.appearance().backgroundColor = UIColor.white
    }

    var body: some Scene {
        WindowGroup {
            TabView {
                NavigationStack {
                    FirstView()
                }
                .tabItem {
                    Text("タブ")
                }
            }
        }
    }
}

次にSecondViewへの遷移処理を実装したFirstViewを以下のように書きます。

FirstView.swift
struct FirstView: View {
    var body: some View {
        VStack(spacing: 24) {
            Text("First View")
                .foregroundStyle(.white)

            NavigationLink {
                SecondView()
            } label: {
                Text("to SecondView")
            }
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .background(.red.opacity(0.6))
    }
}

#Preview {
    FirstView()
}

最後にSecondViewを実装します。
SecondViewではflagという.toolbar(, for:)のvisibilityを変更するためのプロパティを用意します。
このflagを利用して、Backボタンタップ時にvisibilityをFirstViewの表示前にvisibleに変更します。
注意点としては、flagを@Stateで宣言することです。
@Stateで宣言しないと、flagの値が変わってもViewが再描画されないため、前画面に戻った時にタブの表示でラグが発生してしまいます。

SecondView.swift
public struct SecondView: View {
    @Environment(\.dismiss) var dismiss
    @State var flag: Visibility = .hidden

    public var body: some View {
        Text("Second View")
            .foregroundStyle(.white)
        .navigationBarBackButtonHidden(true)
        .toolbar{
            ToolbarItem(placement: .navigationBarLeading){
                Button {
                    flag = .visible
                    dismiss()
                } label: {
                    Text("Back")
                }
            }
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .background(.blue.opacity(0.6))
        .toolbar(flag, for: .tabBar)
    }
}

#Preview {
    SecondView()
}

終わりに

TabViewとNavigationStackの両方を使うことはよくあることであり、push遷移後にフルスクリーンで画面を表示したいという要求もまたよくあることだと思います。
一方でこのタブの表示のラグに関する明確な解決策が見つからず、ラグを受け入れる、あるいはフルスクリーンモーダルで実装などの対応を取られていた方もいるかもしれません。
この解決法がTabViewとNavigationStackの実装の一助になると幸いです。
最後までお読み頂き、ありがとうございました。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2