[SwiftUI]Sheetの閉じかた:Environmentの.presentationModeより@Bindingを渡して行う表示制御が自然に思える

SwiftUI

表示したシートをどうやって閉じるか?という点について改めて確認してみます。

表示したシートを閉じる

以下の2つの方法があります。

  • 表示されたシートを下方向にドラッグして画面から消す
  • Closeボタンを表示して、押されたら閉じる

下方向にドラッグして消す方法

プログラム的には特に何も実装する必要がないです。
OSで適切に処理してくれます。

Closeボタンを表示して押されたら閉じる方法

以下の2つのやり方があります。

  • Environmentの.presentationModeを利用する方法
  • @Bindingで表示制御の変数を渡す方法

大抵の場合、両方の手段を用意すると思います。つまり、OSの処理と並行して、ボタン等を用意して、明示的にクローズする方法も用意すると思います。その実装方法が、今回のポイントです。

Environmentの.presentationModeを利用する方法

以下のコードをsheet側に追加しておきます。

presentationModeの定義

@Environment(\.presentationMode) var presentationMode

Closeボタンのactionに、以下のように記述するとクローズすることができます。

presentationModeを使用して閉じるコード

self.presentationMode.wrappedValue.dismiss()

@Bindingで表示制御の変数を渡す方法

大抵のSheetは、以下のように、表示制御の変数がisPresentedに渡されて表示制御されます。

sheetの設定コード

.sheet(isPresented: $showingSheet, content: {
    SheetWithBinding(showingSheet: self.$showingSheet)
})

この表示制御の変数をSheetに渡すことで、Sheet内部からも表示非表示を制御できるようになります。

Sheet側のコード

struct SheetWithBinding: View {
    @Binding var showingSheet:Bool
    var body: some View {
        VStack {
            Button(action: {
                self.showingSheet.toggle()
            }, label: {Text("Close")})
        }
    }
}

上記のSheetを以下のような形で上位ビューに設定しておくと、EnvironmentのpresentationModeを使用せずに制御できます。

コード

truct ContentView: View {
    @State private var showingSheet = false
    var body: some View {
        VStack {
            Button(action: {
                self.showingSheet.toggle()
            }, label: {
                Text("Show sheet with binding")
            })
        }
        .sheet(isPresented: $showingSheet, content: {
            SheetWithBinding(showingSheet: self.$showingSheet)
        })
    }
}

EnvironmentのpresentationModeを使うと、マジックナンバー感が強いと感じていて、単純に表示制御の変数をBindingに渡して制御する方がわかりやすい気がします。

まとめ:EnvironmentのpresentationModeよりも、Bindingで表示制御変数を渡そう

上にも書きましたが、presentationModeよりも、Bindingで渡された表示変数を制御する方が、読みやすいコードに思います。

今回のサンプルコードは以下のものです。最初に表示されるシートの制御はBinding変数で。2番目に表示されるシートの制御は、EnvironmentのpresentationModeで行われています。

コード


struct ContentView: View {
    @State private var showingSheet = false
    var body: some View {
        VStack {
            Button(action: {
                self.showingSheet.toggle()
            }, label: {
                Text("Show sheet with binding")
            })
        }
        .sheet(isPresented: $showingSheet, content: {
            SheetWithBinding(showingSheet: self.$showingSheet)
        })
    }
}

struct SheetWithBinding: View {
    @Binding var showingSheet:Bool
    @State private var showingSubSheet = false
    var body: some View {
        VStack {
            Button(action: {
                self.showingSheet.toggle()
            }, label: {Text("Close")})
            Button(action: {
                self.showingSubSheet.toggle()
            }, label: {
                Text("SubSheet")
            })
        }
    .sheet(isPresented: $showingSubSheet, content: {
        SheetWithPresentationmode()
    })
    }
}

struct SheetWithPresentationmode: View {
    @Environment(\.presentationMode) var presentationMode
    var body: some View {
        Button(action: {
            self.presentationMode.wrappedValue.dismiss()
        }, label: {Text("Close")})
    }
}

1つのビューには、1つのSheetしか設定できないため、
ビュー ー> @Bindingで制御されるSheet -> EnvironemtnのpresentationModeで制御されるSheet
という階層構造になっています。

説明は以上です。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です