How can I have two alerts on one view in SwiftUI?
I improved a litle Ben's answer. You can show multiple alerts dynamically by using .alert(item:) instead .alert(isPresented:):
struct AlertItem: Identifiable {
var id = UUID()
var title: Text
var message: Text?
var dismissButton: Alert.Button?
}
struct ContentView: View {
@State private var alertItem: AlertItem?
var body: some View {
VStack {
Button("First Alert") {
self.alertItem = AlertItem(title: Text("First Alert"), message: Text("Message"))
}
Button("Second Alert") {
self.alertItem = AlertItem(title: Text("Second Alert"), message: nil, dismissButton: .cancel(Text("Some Cancel")))
}
Button("Third Alert") {
self.alertItem = AlertItem(title: Text("Third Alert"))
}
}
.alert(item: $alertItem) { alertItem in
Alert(title: alertItem.title, message: alertItem.message, dismissButton: alertItem.dismissButton)
}
}
}
There's a variation on this solution which only uses one state variable rather than two. It uses the fact that there is another .alert()
form which takes an Identifiable
item rather than a Bool, so extra information can be passed in that:
struct AlertIdentifier: Identifiable {
enum Choice {
case first, second
}
var id: Choice
}
struct ContentView: View {
@State private var alertIdentifier: AlertIdentifier?
var body: some View {
HStack {
Button("Show First Alert") {
self.alertIdentifier = AlertIdentifier(id: .first)
}
Button("Show Second Alert") {
self.alertIdentifier = AlertIdentifier(id: .second)
}
}
.alert(item: $alertIdentifier) { alert in
switch alert.id {
case .first:
return Alert(title: Text("First Alert"),
message: Text("This is the first alert"))
case .second:
return Alert(title: Text("Second Alert"),
message: Text("This is the second alert"))
}
}
}
}
The second call to .alert(isPresented)
is overriding the first. What you really want is one Binding<Bool>
to denote whether the alert is presented, and some setting for which alert should be returned from the closure following .alert(isPresented)
. You could use a Bool for this, but I went ahead and did it with an enum, as that scales to more than two alerts.
enum ActiveAlert {
case first, second
}
struct ToggleView: View {
@State private var showAlert = false
@State private var activeAlert: ActiveAlert = .first
var body: some View {
Button(action: {
if Bool.random() {
self.activeAlert = .first
} else {
self.activeAlert = .second
}
self.showAlert = true
}) {
Text("Show random alert")
}
.alert(isPresented: $showAlert) {
switch activeAlert {
case .first:
return Alert(title: Text("First Alert"), message: Text("This is the first alert"))
case .second:
return Alert(title: Text("Second Alert"), message: Text("This is the second alert"))
}
}
}
}