見出し画像

SwiftUIで複数のアラートを表示する、連続で表示する

SwiftUIでアラートを表示する

SwiftUIでアラートを表示する基本的な方法

struct ContentView: View {
   // アラート表示フラグ
   @State var showingAlert = false
   
   var body: some View {
       Button {
           showingAlert.toggle()
       } label: {
           Text("アラート")
       }
       // alert modifierにフラグの参照を渡す
       .alert(isPresented: $showingAlert) {
           Alert(title: Text("タイトル"),
                 message: Text("メッセージ"),
                 primaryButton: .default(Text("OK"), action: {
                   // OKボタン押下時の処理
                 }),
                 secondaryButton: .cancel(Text("キャンセル")))
       }
   }
}

複数のアラートを出し分け

1つのボタンを押したときに、条件によって異なるアラートを表示したい場合、以下のようにalert modifierを2つ重ねると、宣言の上書きでアラート2しか表示できない。

struct ContentView: View {
   @State var showingAlert1 = false
   @State var showingAlert2 = false
   
   var body: some View {
       Button {
           // 条件によってアラート1,2を出し分け
           if 条件 {
               showingAlert1.toggle() // 表示されない
           } else {
               showingAlert2.toggle() // 表示される
           }
       } label: {
           Text("アラート")
       }
       // アラート1
       .alert(isPresented: $showingAlert1) {
           Alert(title: Text("アラート1"),
                 message: Text("メッセージ1"),
                 dismissButton: .default(Text("OK")))
       }
       // アラート2
       .alert(isPresented: $showingAlert2) {
           Alert(title: Text("アラート2"),
                 message: Text("メッセージ2"),
                 dismissButton: .default(Text("OK")))
       }
   }
}

これを解決するには以下の方法がある。

① アラートタイプを定義する
② 動的にアラートを生成する

① アラートタイプを定義する

enumのAlertTypeを定義して、1つのalert modifierの中でalertTypeによってAlertを生成して返却する。条件によってalertTypeを設定してから表示フラグをONにすれば、意図したアラートを表示することができる。

// アラートタイプを定義
enum AlertType {
   case alert1
   case alert2
}

struct ContentView: View {
   @State var showingAlert = false
   @State var alertType: AlertType = .alert1
   
   var body: some View {
       Button {
           // 条件によってアラートタイプを設定
           if 条件 {
               alertType = .alert1
           } else {
               alertType = .alert2
           }
           showingAlert.toggle()
       } label: {
           Text("アラート")
       }
       .alert(isPresented: $showingAlert) {
           // アラートタイプによってAlertを返却
           switch alertType {
           case .alert1:
               return Alert(title: Text("アラート1"),
                            message: Text("メッセージ1"),
                            dismissButton: .default(Text("OK")))
           case .alert2:
               return Alert(title: Text("アラート2"),
                            message: Text("メッセージ2"),
                            dismissButton: .default(Text("OK")))
           }
       }
   }
}

② 動的にアラートを生成する

alert(item:content:)というメソッドを使う。Identifiableプロトコルに準拠したAlertItemを定義し、alertItemの内容によってAlertを生成する。条件によってalertItemを変更すれば、意図したアラートを表示できる。

// Identifiableプロトコルに準拠したAlertItemを定義
struct AlertItem: Identifiable {
   var id = "id"
   var title: Text
   var message: Text?
   var dismissButton: Alert.Button?
}

struct ContentView: View {
   @State var alertItem: AlertItem?
   
   var body: some View {
       Button {
           // 条件によってアラートアイテムを生成
           if 条件 {
               alertItem = AlertItem(title: Text("アラート1"), message: Text("メッセージ1"), dismissButton: .default(Text("OK")))
           } else {
               alertItem = AlertItem(title: Text("アラート2"), message: Text("メッセージ2"), dismissButton: .default(Text("OK")))
           }
       } label: {
           Text("アラート")
       }
       // アラートアイテムの参照を渡す ※nilの場合はmodifier closureは実行されない
       .alert(item: $alertItem) { item in
           Alert(title: item.title, message: item.message, dismissButton: item.dismissButton)
       }
   }
}

アラートの連続表示

条件分岐による出し分けではなく、連続でアラートを表示させたい場合、以下の処理をaction closure内で非同期実行すればよい。

① アラートタイプを定義する場合:alertTypeの更新 & 表示フラグON
② 動的にアラートを生成する場合:alertItemの再生成
// アラートタイプを定義する場合
.alert(isPresented: $showingAlert) {
   switch alertType {
   case .alert1:
       return Alert(title: Text("アラート1"),
                    message: Text("メッセージ1"),
                    dismissButton: .default(Text("OK"), action: {
                       // 非同期実行が必要
                       DispatchQueue.main.async {
                           alertType = .alert2
                           showingAlert.toggle()
                       }
                    }))
   case .alert2:
       return Alert(title: Text("アラート2"),
                    message: Text("メッセージ2"),
                    dismissButton: .default(Text("OK")))
   }
}

// 動的にアラートを生成する場合
alertItem = AlertItem(title: Text("アラート1"), message: Text("メッセージ1"), dismissButton: .default(Text("OK"), action: {
   // 非同期実行が必要
   DispatchQueue.main.async {
       // alertItemを更新すればアラートを再表示してくれる
       alertItem = AlertItem(title: Text("アラート2"), message: Text("メッセージ2"), dismissButton: .default(Text("OK")))
   }
}))

参考


この記事が気に入ったらサポートをしてみませんか?