はじめに
Vaporアプリケーション経由でMongoDBに保存された本の情報をListViewに表示させることができました。
表示させただけでは能がないので、本の削除のためのモーダルシートを表示させてみます。
iOS向けでSwiftUIを使ったSheetの表示をしたことがあるので、これの応用になると思います。基礎を押さえておくと応用は(とりあえず)いかようにも利きますね。
準備
前提として
ここまでできたところからのスタートです。
方針
macOS向けのアプリケーション開発初心者が着手前に考えた方針になります。進めていく上で軌道修正かけるかもしれないです。
- モーダルシートにするViewを作る
- モーダルシートを表示させたり閉じたりできるようにする
- モーダルシートにデータを渡す
- モーダルシートにデータが表示されるのを確認する
モーダルシートが表示できるようにする
モーダルシートに表示させるViewを作る
まずはモーダルシートを作ってみます。
今回作りたいのは、本をListViewから選択して、これを削除してよいかどうかの確認ダイアログです。これを踏まえて、データを表示する枠組みを作っていきます。
@Binding var selectedItem:Book?
var body: some View {
VStack {
Text("Delete Book")
.font(.title)
.frame(maxWidth: .infinity, alignment: .leading)
Spacer()
Text(String(selectedItem?.title ?? ""))
.font(.body)
.fontWeight(.bold)
.opacity(1.0)
.truncationMode(.tail)
.frame(maxWidth: .infinity, alignment: .leading)
HStack(alignment: .center) {
Text(String(selectedItem?.publisher ?? ""))
.font(.footnote)
.fontWeight(.regular)
.opacity(0.75)
.truncationMode(.middle)
.frame(maxWidth: .infinity, alignment: .leading)
}
Spacer()
HStack {
Button(action: {
}) {
Text("Delete")
}
Button(action: {
}) {
Text("Close")
}
}
}
.padding(16.0)
.frame(width: 240, height: 160)
}
すでにチラッと出ていますが、モーダルシートの呼び出し元からモーダルシートにデータを渡す時に使うプロパティはこれです。
@Binding var selectedItem:Book?
モーダルシートがデータを管理するのではなく、呼び出し元で管理されるものを共有してもらう形なので、Binding属性です。
プレビューでこのような感じになっています。
モーダルシートを表示させてみる
モーダルシートのViewができましたので、早速これを表示させてみます。ちなみにモーダルシートのViewの名前はConfirmDelete
にしています。
ボタンをクリックするとダイアログが出るようにします。
@State private var showingSheet = false
Button(action: {
self.showingSheet.toggle()
}) {
Text("Delete")
}.sheet(isPresented: self.$showingSheet, onDismiss: {print("シート閉じた")}) {
ConfirmDelete(selectedItem: self.$selectedBook)
}
これで表示することができます。クリックしたボタンが見えませんが、シートの裏に隠れてしまっています。
モーダルシートを閉じてみる
このままでは開いたら閉じることができません。閉じることができるようにします。
モーダルシート上のCloseボタンをクリックした時のアクションを追加しておきます。
@Environment(\.presentationMode) var presentationMode
Button(action: {
self.presentationMode.wrappedValue.dismiss()
}) {
Text("Close")
}
参考までに
デリゲートでモーダルシートの呼出し元に閉じさせるという手もあるかと思います。閉じるまでは開いた者の責任という考え方です。家に帰り着くまでが遠足というのと似ています。えっ?似てない?
モーダルシートからデータを返す時にも、データの処理を呼び出し元に全部任せる事ができる(データ一式を丸投げできる)ので便利です。
本が選択されていない時はモーダルシートを開かない
削除するかどうかの確認ダイアログという位置づけのため、ListViewで本が選択されていなければモーダルシート自体を表示させる必要がありません。選択されていない旨のダイアログを出すアプローチもありますが、今回は単純に表示させない方法を取ります。でも、ダイアログを出したほうが使う人にとっては親切です。
Button(action: {
if let _ = self.selectedBook
{
self.showingSheet.toggle()
}
}) {
Text("Delete")
}.sheet(isPresented: self.$showingSheet, onDismiss: {print("シート閉じた")}) {
ConfirmDelete(selectedItem: self.$selectedBook)
}
ボタンをクリックした時にListViewのセルが選択されているかどうかで判断し、モーダルシートの表示フラグを更新しないようにしています。
確認してみる
ListViewに本の一覧を表示して、モーダルシートを表示させてみます。
Closeをクリックするとこの通り。
もちろん、ListViewで本が選択されていなければ、モーダルシートは表示されません。
まとめ
iOSと同じ方法でmacOSでもモーダルシートを表示させることができました。気になるところがあるとすると、モーダルシートの表示非表示をフラグで制御していることです。モディファイアの都合上、致し方ない部分なんですかね……
気になるDeleteボタン
モーダルシートにはボタンが2つあります。今回はCloseボタンのみに処理を実装し、もう一つのDeleteボタンには何も記述しませんでした。
目標としては、Deleteボタンをクリックすると、ListViewで選択した本をMongoDBから削除したいのです。これを実現するにはモーダルシートを閉じた時に、どのボタンがクリックされたかを判定しないといけません。デリゲートの出番ですかね。