2
votes

I'm trying to show an alert which is triggered by a modal sheet. Here's a small demo project:

import SwiftUI

struct ContentView: View {
    @State private var showSheet = false
    @State private var showAlert = false

    var body: some View {
        Button("Press") {
            showSheet = true
        }
        .sheet(isPresented: $showSheet) {
            Button("Close with alert") {
                showSheet = false
                showAlert = true
            }
        }
        .alert(isPresented: $showAlert) {
            Alert(title: Text("Alert"))
        }
    }
}

After clicking on the "Press" button, a modal sheet appears with a button "Close with alert". If this button is pressed the sheet closes and nothing happens. I expect to have the alert shown.

It seems that the animation of hiding the sheet is causing the issue as SwiftUI doesn't seem to consider the sheet as closed after setting showSheet = false. The following warning appears which is supporting this theory:

[Presentation] Attempt to present <SwiftUI.PlatformAlertController: 0x7fbbab012200> on <TtGC7SwiftUI19UIHostingControllerGVS_15ModifiedContentVS_7AnyViewVS_12RootModifier_: 0x7fbbaa60b7d0> (from <TtGC7SwiftUI19UIHostingControllerGVS_15ModifiedContentVS_7AnyViewVS_12RootModifier_: 0x7fbbaa60b7d0>) which is already presenting <TtGC7SwiftUI22SheetHostingControllerVS_7AnyView: 0x7fbbaa413200>.

2

2 Answers

6
votes

You can use onDismiss.

Here are some examples based on when do you want to present an alert:

  1. Always close with an alert:
struct ContentView: View {
    @State private var showSheet = false
    @State private var showAlert = false

    var body: some View {
        Button("Press") {
            showSheet = true
        }
        .sheet(isPresented: $showSheet, onDismiss: {
            showAlert = true
        }) {
            Button("Close") {
                showSheet = false
            }
        }
        .alert(isPresented: $showAlert) {
            Alert(title: Text("Alert"))
        }
    }
}
  1. Close with an alert on button click only:
struct ContentView: View {
    @State private var showSheet = false
    @State private var showAlert = false
    @State private var closeSheetWithAlert = false

    var body: some View {
        Button("Press") {
            showSheet = true
            closeSheetWithAlert = false
        }
        .sheet(isPresented: $showSheet, onDismiss: {
            showAlert = closeSheetWithAlert
        }) {
            Button("Close") {
                closeSheetWithAlert = true
                showSheet = false
            }
        }
        .alert(isPresented: $showAlert) {
            Alert(title: Text("Alert"))
        }
    }
}
0
votes

You need to give time for sheet been closed, so do it with delay, like

    .sheet(isPresented: $showSheet) {
        Button("Close with alert") {
            showSheet = false
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
               showAlert = true
            } 
        }
    }

Note: also worth considering doing this in onDismiss sheet callback, but this would require your states model rethink.