Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple Alerts in one view can not be called SwiftUI

Tags:

ios

swift

swiftui

The method of calling an alert in swiftUI is supposed to be simpler than ever, a simple example of this would be

struct ContentView: View {
    @State private var showingAlert = false

    var body: some View {
        Button(action: {
            self.showingAlert = true
        }) {
            Text("Show Alert")
        }
        .alert(isPresented: $showingAlert) {
            Alert(title: Text("Important message"), message: Text("Wear sunscreen"), dismissButton: .default(Text("Got it!")))
        }
    }
}

However, even though I am calling an alert and can verify the method is being called, my alert does not display.

In my code I have the following Struct

private struct resetButton: View {
    @State var isResetting = false

    var body: some View {
        Button("Reset"){
            self.isResetting = true
        }.alert(isPresented: $isResetting) {
            print("We get here")
            return
                Alert(title: Text("Are you sure?"), message: Text("This will erase your Access Key."), primaryButton: .destructive(Text("Reset")) {
                    clearToken()
                    clearAccessKey()
                    clearUsername()
                    }, secondaryButton: .cancel(Text("Cancel")))
        }
    }
}

Which is called in the main view simply by resetButton().

However, when I push this button, despite isResetting equalling true, the alert does not display. I have verified that my .alert method is being called as the console prints

we get here

the first time the button is pushed.

However, the Alert is never displayed.

An alert is displayed correctly elsewhere in this same view with

.alert(isPresented: $failedLogin) {
                        self.password = ""
                        return
                            Alert(title: Text("Invalid Login"), message: Text("Please verify your credentials and try again"), dismissButton: .default(Text("Ok")))
                    }

When a @State variable named failedLogin is set true, however, the aforementioned alert is never presented.

My first impression is that this may be a bug in Swift, if it is I'll report it to apple. However, maybe something I'm doing isn't working because of an error on my part.

Edit:

As clarified in the answer below, the problem seems to be relating to the fact that my resetButton() was trying to throw up an alert in a view that already contains an alert. Apparently you can't have multiple alerts in one view.

like image 991
Vapidant Avatar asked Nov 30 '19 12:11

Vapidant


People also ask

How do I show custom alerts in SwiftUI?

Build and present custom alerts to your users Presenting system alerts on SwiftUI is super simple. Just call an instance method alert and pass a few parameters, and you are done.

How do I show messages in SwiftUI?

We can show alerts to the user in SwiftUI with an alert() modifier. Present without data. Present with data.


2 Answers

You can show 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)
        }
    }
}
like image 186
aslebedev Avatar answered Oct 11 '22 10:10

aslebedev


So i'm still not 100% sure why this was unable to post an alert this way? But I was able to fix this issue simply by placing the alert in the main view.

I changed the @State variable isResetting to a binding bool and paced a new state variable in the main class. I then just copied over the .alert and this seems to solve the issue.

The struct now looks like this

private struct resetButton: View {
    @Binding var isResetting: Bool

    var body: some View {
        Button("Reset"){
            self.isResetting = true
        }

    }
}

and the alert is the same but is now in the class calling resetButton().

Edit:

So it appears that SwiftUI won't let you call multiple alerts from the same view, however, if you want multiple alerts in the same view then there is a way around this.

You can call an alert from anywhere inside of a view so the following solution works.

private struct exampleAlert: View {
    @State var alert1 = false
    @State var alert2 = false

    var body: some View {
        Vstack{
           Button("alert 1"){
             self.alert1 = true
           }.alert(isPresented: $alert1) {
            Alert(title: Text("Important message"), message: Text("This alert can show"), dismissButton: .default(Text("Got it!")))
        }
           Button("alert 2"){
             self.alert2 = true
           }.alert(isPresented: $alert2) {
            Alert(title: Text("Important message"), message: Text("This alert can also show"), dismissButton: .default(Text("Got it!")))
        }
        }
    }
}

The catch is here that if either of these alerts were to be placed on the Vstack, one will not function. If they are placed on separate views though then both can be called as expected.

Perhaps this is something that will be rectified in a future update? In the meantime though, here is a solution for working around this problem.

like image 7
Vapidant Avatar answered Oct 11 '22 10:10

Vapidant