ひとつのViewから複数の画面をモーダルへ遷移したい。ボタンを複数用意して、それぞれ対応する画面をモーダル表示させようとしたがうまくいかない。
.sheet()
modifierを設定すれば、モーダル表示できることは知っているが、それぞれ異なる複数の画面への遷移の場合はうまくいかない。
実行環境
- Xcode 11.6
- iOS 13.6
問題の挙動
ひとつの画面上に「画像を選択するボタン」と「画像を切り出す(クロッピングする)ボタン」を用意した。
それぞれのボタンを押した時に、対応する画面をモーダル表示させたい。
import SwiftUI struct SampleView : View { @State private var sheetIsPresented1 = false @State private var sheetIsPresented2 = false var body: some View { VStack { Button(action: { self.sheetIsPresented1 = true }, label: { Text("modal 1 (show picker)") }) Button(action: { self.sheetIsPresented2 = true }, label: { Text("modal 2 (show cropper)") }) } .sheet(isPresented: $sheetIsPresented1) { ImagePickerView(sourceType: .photoLibrary, onCanceled: { // on cancel }) { (image) in // on success } } .sheet(isPresented: $sheetIsPresented2) { ImageCropView(originalImage: UIImage(), onCanceled: { // on cancel }) { (image) in // on success } } } }
modal 2ボタンはうまく反応するが、modal 1ボタンを押しても反応しなくなってしまった。どうすれば解決できるのか?
解決編
解決方法はふたつある。
ボタンにそれぞれ .sheet() メソッドを紐づける
ボタンに対して、処理が明確になるので個人的には好きだが、bodyプロパティがごちゃごちゃしてしまいがち。
import SwiftUI struct SampleView : View { @State private var sheetIsPresented1 = false @State private var sheetIsPresented2 = false var body: some View { VStack { Button(action: { self.sheetIsPresented1 = true }, label: { Text("modal 1 (show picker)") }) .sheet(isPresented: $sheetIsPresented1) { ImagePickerView(sourceType: .photoLibrary, onCanceled: { // on cancel }) { (image) in // on success } } Button(action: { self.sheetIsPresented2 = true }, label: { Text("modal 2 (show cropper)") }) .sheet(isPresented: $sheetIsPresented2) { ImageCropView(originalImage: UIImage(), onCanceled: { // on cancel }) { (image) in // on success } } } } }
.sheet()を 一箇所で定義する場合
どんなシートかを明示しておく必要があるが、処理する場所をまとめておくことができる。たとえば、モーダルA→モーダルBなど連続して画面を変えたい場合など、ボタンに紐付かない処理もこの方法でならスムーズに実装できる。
struct SampleView : View { enum SheetType { case imagePick case imageCrop } @State private var currentSheet: SheetType = .imagePick @State private var sheetIsPresented = false var body: some View { VStack { Button(action: { self.currentSheet = .imagePick self.sheetIsPresented = true }, label: { Text("modal 1 (show picker)") }) Button(action: { self.currentSheet = .imageCrop self.sheetIsPresented = true }, label: { Text("modal 2 (show cropper)") }) } .sheet(isPresented: $sheetIsPresented) { if (self.currentSheet == .imagePick) { ImagePickerView(sourceType: .photoLibrary, onCanceled: { // on cancel }) { (image) in // on success } } else if (self.currentSheet == .imageCrop) { ImageCropView(originalImage: UIImage(), onCanceled: { // on cancel }) { (image) in // on success } } } } }
以上で、ひとつのViewから複数のシートを表示することができるようになった。