💡

[SwiftUI]複数のアラートを表示させる

2022/05/02に公開約3,800字

Alertの複数表示

  • たまに忘れてしまって焦るが、Alertは複数記述すると、後に書いたものに上書きされてしまい、最後のもの以外は反応しなくなる
  • 下記のサンプルのケースでは、「Button 2」はアラートが表示されるが、「Button 1」はボタンを押しても何も起こらない
struct ContentView: View {
  @State var isPresentingWarning = false
  @State var isPresentingFatal = false

  var body: some View {
    NavigationView {
      VStack {
        Button(action: {
          isPresentingWarning.toggle()
        }) {
          Text("Button 1")
        }
        .padding()

        Button(action: {
          isPresentingFatal.toggle()
        }) {
          Text("Button 2")
        }
        .padding()
        
        Spacer()
      }
    }
    .alert(isPresented: $isPresentingWarning) {
      Alert(title: Text("Warning......"),
            dismissButton: .default(Text("OK")))
    }
    .alert(isPresented: $isPresentingFatal) {
      Alert(title: Text("Fatal Error."),
            dismissButton: .default(Text("OK")))
    }
  }
}

動的に読み分ける

  • 対応はいくつかあるが、必要なアラートのタイプをenumなどに定義しておき、alertの中で読み分ける方法が分かりやすい
struct ContentView: View {
  enum AlertType {
    case warning
    case fatal
  }

  @State var isPresentingAlert = false
  @State var alertType: AlertType = .warning

  var body: some View {
    NavigationView {
      VStack {
        Button(action: {
          alertType = .warning
          isPresentingAlert.toggle()
        }) {
          Text("Button 1")
        }
        .padding()

        Button(action: {
          alertType = .fatal
          isPresentingAlert.toggle()
        }) {
          Text("Button 2")
        }
        .padding()

        Spacer()
      }
    }
    .alert(isPresented: $isPresentingAlert) {
      switch alertType {
      case .warning:
        return Alert(title: Text("Warning......"),
                     dismissButton: .default(Text("OK")))
      case .fatal:
        return Alert(title: Text("Fatal Error."),
                     dismissButton: .default(Text("OK")))
      }
    }
  }
}

ViewModifierにする

  • 下記のように、ViewModifierにタイプとそれに応じたアラートの挙動を定義し、ViewからはそのViewModifierを呼び出せば良いようにしておくと便利
struct CustomAlert: ViewModifier {
  enum AlertType {
    case warning(message: String?)
    case fatal(message: String?)
  }

  @Binding var isPresentingAlert: Bool
  let type: AlertType

  func body(content: Content) -> some View {
    content.alert(isPresented: $isPresentingAlert) {
      switch type {
      case let .warning(message):
        return Alert(title: Text("Warning"),
                     message: Text(message ?? ""),
                     dismissButton: .default(Text("OK")))
      case let .fatal(message):
        return Alert(title: Text("Fatal Error."),
                     message: Text(message ?? ""),
                     dismissButton: .default(Text("OK")))
      }
    }
  }
}

extension View {
  func customAlert(isPresentingAlert: Binding<Bool>, type: CustomAlert.AlertType) -> some View {
    modifier(CustomAlert(isPresentingAlert: isPresentingAlert, type: type))
  }
}
struct ContentView: View {
  @State var isPresentingAlert = false
  @State var alertType: CustomAlert.AlertType = .warning(message: nil)

  private let warningMessage = ""
  private let fatalErrorMessage = ""

  var body: some View {
    NavigationView {
      VStack {
        Button(action: {
          alertType = .warning(message: warningMessage)
          isPresentingAlert.toggle()
        }) {
          Text("Button 1")
        }
        .padding()

        Button(action: {
          alertType = .fatal(message: fatalErrorMessage)
          isPresentingAlert.toggle()
        }) {
          Text("Button 2")
        }
        .padding()

        Spacer()
      }
    }
    .customAlert(isPresentingAlert: $isPresentingAlert,
                 type: alertType)
  }
}

reference: https://capibara1969.com/2758/

Discussion

ログインするとコメントできます