johnpatrickmorgan / FlowStacks

FlowStacks allows you to hoist SwiftUI navigation and presentation state into a Coordinator
MIT License
857 stars 66 forks source link

Toolbar button presenting sheet only works once on iOS... #53

Closed ConfusedVorlon closed 10 months ago

ConfusedVorlon commented 1 year ago

this is a wierd one. My guess is that it is a swiftUI bug. I'm mostly posting here just so future folks can find my workaround. (though if a fix is possible - that would be great!)

Very simple setup;

Home screen embedded in navigation view

Home screen has a navbar button which presents a sheet

Click on the button - the sheet presents. Swipe it down, it dismisses (the dismiss callback is called correctly)

Now you can't click on the toolbar button (though it doesn't show as disabled)

My fix is simply to change the id on the home view. That re-draws it and re-enables the toolbar. Bug doesn't happen on iPad - go figure...

Am I missing something, or a better fix???

enum Screen {
  case home
  case sheet
}

struct ContentView: View {
    @State var routes: Routes<Screen> = [.root(.home,embedInNavigationView: true)] {
        didSet {
            print("routes: \(routes)")
        }
    }

    @State var homeId:UUID = UUID()

    var body: some View {
        Router($routes) { screen, _ in
            switch screen {
            case .home:
                Home(showSheet: showSheet)
                //hacky fix
                //uncomment the .id to fix the issue
                //change the id here to re-enable the toolbar after a the sheet is dismissed...
                //    .id(homeId)
            case .sheet:
                Sheet()
            }
        }
    }

    func showSheet(){
        routes.presentSheet(.sheet) {
            print("dismiss")
            //hacky fix
            homeId = UUID()
        }
    }
}
struct Home: View {
    var showSheet:()->Void

    var body: some View {
        VStack
        {
            Text("Home")

            //this button always works
            Button {
                showSheet()
            } label: {
                Text("Show Sheet")
            }

        }
            .toolbar {
                ToolbarItem(placement: .navigationBarTrailing) {
                    //this button only works once (without the id fix)
                    Button {
                        showSheet()
                    } label: {
                        Text("Sheet")
                    }

                }
            }
    }
}
struct Sheet: View {
    var body: some View {
        Text("Sheet")
    }
}
johnpatrickmorgan commented 1 year ago

Thanks for sharing this @ConfusedVorlon! I just tried to reproduce this using your example code, but failed: everything worked fine without the ID workaround. I tried using Xcode 14.2 on simulator: both on iOS 14.5 and iOS 16.2. Was your setup different?

ConfusedVorlon commented 1 year ago

that's weird. here's my sample project: https://www.dropbox.com/s/0m85n40cwbb9n68/SheetFail.zip?dl=0

to be clear - the button in the main view always works. The toolbar button (top right) only works once.

XCode 14.2, iOS 16.2 or 15.5 (simulator) Works fine on iPad simulator

ConfusedVorlon commented 1 year ago

I have ended up pulling out the flowstacks wrapper as it it seems to be incompatible with using the newer NavigationSplitView on MacOS

I see exactly the same issue with the toolbar button only working once even when there is no flowstacks routing at all. I wish SwiftUI was more solid...

ConfusedVorlon commented 1 year ago

fyi - the incompatibility on MacOS:

I thought this would let me present sheets/etc using FlowStacks (which would be really nice) Unfortunately, once you present, then hide a sheet - the show/hide sidebar button disappears in mac.

I guess I shouldn't be surprised that NavigationSplitView objects to being inside the router as (presumably) you're using NavigationView around it at some level...

ViacheslavKorkhonen commented 1 year ago

For iOS: A simple workaround is to add .frame(height: 96) to the Text("Sheet") in the Button