Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Issue with SwiftUI displaying Alert – Will not display alert box

I have the following code:

@State private var signoutAlert = false

var body: some View {

    Button(action: {
      
        self.signoutAlert = true
        
        print("signout button clicked")
        
    }) {
        
        Text("Sign Out")
    
    }
    .alert(isPresented: $signoutAlert) {
        
        print(".alert will display")
        //
        return Alert(title: Text("Sign Out"), message: Text("Are you sure you want to Sign Out?"), primaryButton: .destructive(Text("Sign Out")) {
            
            print("Signing out....")
          
            self.session.signOut()

            self.presentationMode.wrappedValue.dismiss()
            
        }, secondaryButton: .cancel())
    
    }

}

The following output prints out:

  1. signout button clicked
  2. .alert will display

I'm expecting the Alert Box to display and prompt the user to either "Cancel" or "Sign Out" by click one of the two buttons; but it is never displayed or prompts the user, which makes no sense!?

Does anyone see anything wrong with my code!? This is extremely frustrating since it should be very simple!?

like image 533
Learn2Code Avatar asked Jul 06 '20 01:07

Learn2Code


3 Answers

I've had this problem when there is an .alert(...) view modifier on a view further up the view hierarchy. If a parent view has an .alert() defined on it then a child's .alert() modifier will not display it's alert.

In my case I moved the .alert() on the high level view down to be a modifier on the Button that triggered it, and now both alerts display correctly. For example:

struct HiddenWalrusView: View {
    @State private var showAlert1 = false
    @State private var showAlert2 = false
    
    var body: some View {
        VStack {
            Button("Show Alert One") { showAlert1 = true }
                .padding(20)
            Button("Show Alert Two") { showAlert2 = true }
                .padding(20)
                .alert(isPresented: $showAlert2) {
                    // This alert never displays
                    Alert(title: Text("I am the Walrus"))
                }
        }
        .alert(isPresented: $showAlert1) {
            Alert(title: Text("I am the Egg Man"))
        }
    }
}

struct EggmanAndWalrusView: View {
    @State private var showAlert1 = false
    @State private var showAlert2 = false
    
    var body: some View {
        VStack {
            Button("Show Alert One") { showAlert1 = true }
                .padding(20)
                .alert(isPresented: $showAlert1) {
                    Alert(title: Text("I am the Egg Man"))
                }
            Button("Show Alert Two") { showAlert2 = true }
                .padding(20)
                .alert(isPresented: $showAlert2) {
                    Alert(title: Text("I am the Walrus"))
                }
        }
    }
}
like image 59
Ken Chin-Purcell Avatar answered Oct 24 '22 03:10

Ken Chin-Purcell


I had the same problem but realized this is now a deprecated .alert modifier. When I moved to using the newer modifier, it worked fine. Example from Apple's documentation:

        .alert(title, isPresented: $didFail) {
            Button("OK") {
                // Handle acknowledgement.
            }
        } message: {
            Text("Please ensure your credentials are correct.")
        }
like image 23
tdl Avatar answered Oct 24 '22 05:10

tdl


Thanks to @ken-chin-purcell, his answer helps me to find more convenient workaround.

Just put alert under EmptyView:

    .overlay(
        EmptyView()
            .alert(isPresented: $showAlert1) {
                Alert(
                    title: ...,
                    message: ...,
                    ...
                )
            },
        alignment: .bottomTrailing
    )
    .overlay(
        EmptyView()
            .alert(isPresented: $showAlert2) {
                Alert(
                    title: ...,
                    message: ...,
                    ...
                )
            },
        alignment: .bottomTrailing
    )

Or use extension:

extension View {
    public func alertPatched(isPresented: Binding<Bool>, content: () -> Alert) -> some View {
        self.overlay(
            EmptyView().alert(isPresented: isPresented, content: content),
            alignment: .bottomTrailing
        )
    }
}
like image 29
Pavel Shorokhov Avatar answered Oct 24 '22 05:10

Pavel Shorokhov