rundfunk47 / stinsen

Coordinators in SwiftUI. Simple, powerful and elegant.
MIT License
904 stars 93 forks source link

Setting navigationBarTitleDisplayMode doesn't work for modal routing. #71

Open polszacki-tooploox opened 2 years ago

polszacki-tooploox commented 2 years ago

When presenting a modal view using router.route(to: \.newCall) the navigation title is always displayed as .large even though the view has .navigationBarTitleDisplayMode set to .inline. It doesn't happen when the same view is presented using standard SwiftUI .sheet.

iPhone

View presented with router.route(to: \.newCall): View presented with .sheet:
Simulator Screen Shot - iPhone 13 Pro - 2022-06-02 at 13 32 22 Simulator Screen Shot - iPhone 13 Pro - 2022-06-02 at 13 32 20

Code from the parent view and the coordinator:

struct TestView: View {
    @State var isPresented = false
    @EnvironmentObject private var router: TestCoordinator.Router

    var body: some View {
        VStack {
            Spacer()
            Button("Test sheet") { isPresented.toggle() }
                .sheet(isPresented: $isPresented, content: {
                    NavigationView {
                        NewCallView()
                            .navigationTitle("New call")
                            .navigationBarTitleDisplayMode(.inline)
                    }
                })
            Spacer()
            Button("Test Stinsen") {
                router.route(to: \.newCall)
            }
            Spacer()
        }
    }
}

final class TestCoordinator: NavigationCoordinatable { 
    (...)
        @Route(.modal) var newCall = makeNewCallView

        @ViewBuilder
        func makeNewCallView() -> some View {
            NavigationView {
                NewCallView()
                    .navigationTitle("New call")
                    .navigationBarTitleDisplayMode(.inline)
            }
        }
    }
}
rundfunk47 commented 2 years ago

Hi, there is a branch that might solve this issue: navbarless. Could you please test it out and see if it works? I'm not 100% done with it yet, so I haven't merged it...

polszacki-tooploox commented 2 years ago

Thanks for the reply. It works on the navbarless branch.

polszacki-tooploox commented 2 years ago

Do you have any ETA on the branch to be merged in or at least stable for production?

rundfunk47 commented 2 years ago

There are still some bugs left to fix (try for instance presenting a modal screen and then pushing a new one), I'll look into it this week and hopefully fix it.

LePips commented 2 years ago

To work with the current state of Stinsen (and how I think it should properly be), instead of creating an explicit NavigationView to present, return a NavigationViewCoordinator<NewCallCoordinator>. This will require creating a new NewCallCoordinator that conforms to NavigationCoordinatable and might just have a single view but this pattern has worked for me for a long time.

Change from your example to:

final class TestCoordinator: NavigationCoordinatable { 
    (...)
    @Route(.modal) var newCall = makeNewCallView

    func makeNewCallView() -> NavigationViewCoordinator<NewCallCoordinator> {
        NavigationViewCoordinator(NewCallCoordinator())
    }
}

and create:

final class NewCallCoordinator: NavigationCoordinatable {

    let stack = NavigationStack(initial: \NewCallCoordinator.start)

    @Root
    var start = makeStart

    @ViewBuilder
    func makeStart() -> some View {
        Text("Hello There")
            .navigationBarTitleDisplayMode(.inline) // This will be inlined
            .navigationTitle("Inline Me")
    }
}

Names are just used for examples. I have used this pattern ever since I used Stinsen as I feel it's how its supposed to work by handling the NavigationView creation to our coordinator pattern. We shouldn't have to create our own NavigationView at all. This does increase some code boilerplate and files but conforms to the pattern a lot better and makes view creation and routing more declarative.

rundfunk47 commented 2 years ago

I agree here. Modally presenting a NavigationView is not really the "Stinsen"-esque way. Usually you'd want to present a Coordinator here. This might be a bit tricky to fix since Stinsen needs to "inject" it's navigation-logic inside the view presented by the NavigationView, not the navigationview itself... I haven't had time to look for an alternative solution yet, but this is probably the top priority issue I want to attempt to fix at least.

But as @LePips says, there are workarounds that are "better".