Open2

SwiftUI: sheetとNavigationStackで同じビューを使いたいときに「閉じる」を付けたいという話

kabeyakabeya

.sheetと、NavigationStackで同じビューを表示したい、というケースがあるかどうか分かりませんが、あったとします。
というか、NavigationStackは重要ではありません。

.sheetで表示するビューに「閉じる」ボタンを付けたい、というケースがあるかどうか分かりませんが、あったとします。

ボタンを付けて、押されたらdismissを呼ぶだけなんですが、「気軽に付けたり外したりしたい。いちいち元のビューに手を入れたくない」という気がするのです。

NavigationStackで表示された先のビューに閉じるボタンが付いてたらおかしいので、ビューにはボタンを付けたくない。でも、もしNavigationStack.sheetとどっちがいいか分からないときに、簡単に試せたらいいなという話でした)

というサンプルです。動きは以下のようになります。

コードは以下のようになります。

struct ContentView: View {
    @State var sheetPresented: Bool = false
    @State var closableSheetPresented: Bool = false
    var body: some View {
        NavigationStack {
            NavigationLink("navigationDestinationを表示", destination: {
                SecondView()
            })
            Button("sheetを表示") {
                sheetPresented.toggle()
            }
            Button("closableSheetを表示") {
                closableSheetPresented.toggle()
            }
        }
        .sheet(isPresented: $sheetPresented) {
            SecondView()
        }
        .sheet(isPresented: $closableSheetPresented) {
            ClosableSheet {
                SecondView()
            }
        }
        .padding()
    }
}

struct SecondView: View {
    var body: some View {
        Form {
            Text("これがSecondViewなんです!")
        }
    }
}

struct ClosableSheet<ViewT>: View where ViewT : View {
    var content: () -> ViewT
    @Environment(\.dismiss) var dismiss
    
    init(@ViewBuilder content: @escaping () -> ViewT) {
        self.content = content
    }
    
    var body: some View {
        VStack {
            HStack {
                Spacer()
                Button("閉じる") {
                    dismiss()
                }
                .buttonStyle(.bordered)
                .font(.caption)
            }
            .padding(8)
            .background(Color(white: 0.9))
            content()
                .padding(.top, -8)
        }
    }
}

.sheetで表示しているビューをClosableSheetで囲んでやるというだけで閉じるボタンが付きます。

kabeyakabeya

もともとは、以下のスクラップに記載してある「TabViewの5個目以降のボタンが勝手にNavigationStackからの遷移になってしまう」という話が発端です。

https://zenn.dev/kabeya/scraps/cd23d7bb51a487

5個目以降のボタンから遷移するビューにNavigationStackがあると、2段のNavigationStackになってしまって非常にややこしいイメージになります。

なので、.sheetに切り替えてみるか、というようになるのですが、元々「戻る」ボタンがある想定のビューなので、上部に何にもないと、途端にどう操作してよいか分からない感じになってしまいます。

もっとちゃんとUI設計しろという話ではあると思うのですが。