ActionSheet and Modal seems have bug in NavigationView

Hello!

I want to use ActionSheet or Modal in NavigationView, but I find something wrong.

If I use ActionSheet in NavigationView, when I click the Cancel button, thIe ActionSheet will appear again.

If I use Modal in NavigationView, when I drag it down to dismiss, the Modal will appear again and again.

Can anyone give me some advice? Thanks a lot.

You never set showActionSheet back to false, so it will continually present.

It's useful to provide code snippet that would need minimum interaction to reproduce the problem. In this case, the ContentView body

struct ContentView: View {
    @State var showActionSheet = false

    var body: some View {
        NavigationView {
            Button(action: { self.showActionSheet = true }) {
                Text("Click")
            }
            .actionSheet(isPresented: $showActionSheet) {
                ActionSheet(title: Text("This should be presented once (Button)"))
            }
            .navigationBarTitle("Title")
            .navigationBarItems(trailing: Button(action: { self.showActionSheet = true }) {
                Text("Show")
            }.actionSheet(isPresented: $showActionSheet) {
                ActionSheet(title: Text("This should be presented once (Navigation View)"))
            })
        }
    }
}

I couldn't reproduce the problem in actual iOS (I don't have Catalina), so it could be Preview bug. In fact, when there is a runtime warning

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.
SingleViewTest[] Warning: Attempt to present <UIAlertController>  on <UINavigationController> which is already presenting (null)

Which could be the cause, that Preview doesn't know how to respond to this.

Let me explain:

You tried to present the actionSheet twice, once from view Button, and again from navigation bar Button.

When you click either button, showActionSheet will become true, and so both actionSheets will try to present themselves, but there can be only one actionSheet presented at any time, hence the warning. You should instead put actionSheet in a single place so that when either button is clicked, only one actionSheet will be presented.

actionSheet set value to false on dismissal.

SwiftUI still has many issues with modal sheets of all kinds from within a NavigationView hierarchy. If you ensure the action that brings up the sheet and the sheet itself are outside the NavigationView hierarchy, sheets work as intended.

1 Like

Thanks for reply.

I delete the Button view and keep the button in NavigationBarItems

struct ContentView: View {
    @State var showActionSheet = false
    
    var body: some View {
        NavigationView {
            Text("Hello World!")
            .navigationBarTitle(Text("Test"))
            .navigationBarItems(trailing: Button(action: {
                self.showActionSheet = true
            }) {
                Text("ADD")
            }.actionSheet(isPresented: $showActionSheet) {
                ActionSheet(title: Text("This is a test"))
            })
        }
    }
}

The ActionSheet still will appear again after I click the cancel button, both in preview and simulator.

I just fresh install Catalina with Xcode 11.1 (11A1027). So I tried your code on preview and simulator, and it only appear once. Maybe it's fixed on later version of Xcode? I'm not too sure either.

Thanks! I'm still using Xcode11.0(11A420a). I'll update it to see whether it works fine in new Xcode.

Regardless Xcode version, which iOS version do you test that with? Try testing the application on a real device with the latest iOS version. Avoid iOS 13.0 because it has non obvious SwiftUI bugs.

It's going to be behave like this for two reasons:

  1. The button that sets self.showActionSheet is in navigationBarItems. If you create a vStack and put the button and NavigationView in it, then this is the first step to circumventing the problem.

  2. The actionSheet is in the NavigationView hiearchy. Make actionSheet part of the vStack you put the button and NavigationView in, and it will work, every time.

1 Like

Is it the current recommendation, to put modal views & the triggers outside NavigationView, or is it only to circumvent an existing bug?

To me it looks too limited since different views in the hierarchy would want to have different set of actionSheet.

This is a workaround for the various maladies that have plagued sheet, throughout beta and even now.

I have not had to deal with the use case of child views having to activate a modal sheet. However, you raise a fair point.

2 Likes

It really works putting ActionSheet outside NavigationView

I was not able to dismiss a sheet until I put it outside of NavigationView. So I guess it is still an issue over a year later, with Xcode 12.5 SwiftUI.