Open2
SwiftUI: sheetとNavigationStackで同じビューを使いたいときに「閉じる」を付けたいという話
.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
で囲んでやるというだけで閉じるボタンが付きます。
もともとは、以下のスクラップに記載してある「TabView
の5個目以降のボタンが勝手にNavigationStack
からの遷移になってしまう」という話が発端です。
5個目以降のボタンから遷移するビューにNavigationStack
があると、2段のNavigationStack
になってしまって非常にややこしいイメージになります。
なので、.sheet
に切り替えてみるか、というようになるのですが、元々「戻る」ボタンがある想定のビューなので、上部に何にもないと、途端にどう操作してよいか分からない感じになってしまいます。
もっとちゃんとUI設計しろという話ではあると思うのですが。