ARTICLE AD BOX
I have created a reusable ModalDialog view that presents modal content via .fullScreenCover. Inside this dialog, a button triggers a SwiftUI .alert.
This works fine in many situations. However, in a specific view hierarchy, the .alert appears for a split second and is immediately dismissed again.
In the code below, the .alert only fails when used inside the exact hierarchy used in the demo code. Removing any one of the following makes the alert work again:
Remove the surrounding TabView Remove the surrounding VStack Remove the NavigationStack Remove .navigationDestination Present the dialog directly from the tab page instead of inside the sheetHowever, in my real app all of these view layers are required and serve a purpose, so I cannot remove them. For example the SettingsContentView does not only show dialogs but can also navigate to other views by pushing content to the navigationPath.
What exactly causes this alert to be dismissed immediately? Is this a SwiftUI bug, or is something fundamentally wrong with my code?
// App Entry @main struct SwiftAppApp: App { var body: some Scene { WindowGroup { ContentView() } } } // Base Content View struct ContentView: View { var body: some View { // Alert failes when TabView is used. // Without the TabView the .alert works fine TabView() { TabPageView() } } } // Page View struct TabPageView: View { @State private var isPresented: Bool = false @State private var showDlg: Bool = false var body: some View { // Removing this sourrounding VStack also 'solves' the problem VStack { Button("Show Settings Sheet") { isPresented = true } .sheet(isPresented: $isPresented) { SettingsSheetView() .background(.red) } // When presented here, the dialog and the alert works fine. Button ("Show Dialog") { showDlg = true } ModalDialogView(isPresented: $showDlg) } } } // Sheet and Content struct SettingsSheetView: View { @State private var path = NavigationPath() var body: some View { // Alert failes when NavigationStack is used. // Without the NavigationStack the .alert works fine NavigationStack(path: $path) { SettingsContentView(path: $path) } } } struct SettingsContentView: View { let path: Binding<NavigationPath> @State var showDlg: Bool = false var body: some View { Button("Show Dlg") { showDlg = true } //Button("Navigate to other view") { // path.append(...) //} ModalDialogView(isPresented: $showDlg) // Alert failes when navigationDestination is used. // Without the navigationDestination the .alert works fine .navigationDestination(for: MySettingsRoute.self) { route in EmptyView() } } } public struct MySettingsRoute: Hashable { } // Dialog with Alert struct ModalDialogView: View { @Binding var isPresented: Bool @State private var showAlert: Bool = false var body: some View { ModalDialog(isPresented: $isPresented) { Button("Show Alert") { showAlert = true } .buttonStyle(.borderedProminent) .alert("Title", isPresented: $showAlert) { EmptyView() } message: { Text("Message") } } } } struct ModalDialog<Content: View>: View { @Binding var isPresented: Bool let content: Content init( isPresented: Binding<Bool>, @ViewBuilder content: () -> Content ) { self._isPresented = isPresented self.content = content() } var body: some View { ZStack{} .fullScreenCover(isPresented: $isPresented) { content .padding() .background(.yellow) .presentationBackground(.clear) } } }