Search code examples
swiftuinavigationview

Only Back Button Visible on Custom Navigation Bar SwiftUI


I have a NavigationView at one of the previous views. But if I do not add another navigation view to this view I only see a navbar with default < Back button.

When I add navigation view to this view then I have double navigation bars

  • one with < Back button
  • one which I created

could not find a way how to get rid of this problem.

struct MainPageView: View {
@Environment(\.presentationMode) var mode: Binding<PresentationMode>
let screenWidth = screenSize.width
var sections = ["deniz", "kara"]
@State private var sectionIndex = 0
var body: some View {
 ZStack {
                Color(UIColor(currentSection()))
                    .edgesIgnoringSafeArea(.all)
                ScrollView(.vertical, showsIndicators: false) { ... }
            }
            .navigationBarTitle("", displayMode: .large)
            .navigationBarBackButtonHidden(true)
            .navigationBarItems(
                leading: HStack{
                    VStack {
                        ZStack {
                            Rectangle()
                                .fill(Color.clear)
                                .frame(width: screenWidth / 2, height: 50)
                            Section {
                                Picker(
                                    selection: $sectionIndex,
                                    label: Text("Sections")
                                ) {
                                    ForEach(0 ..< sections.count) {
                                        Text(self.sections[$0])
                                    }
                                }.pickerStyle(SegmentedPickerStyle())
                            }.padding(.horizontal, 10)
                        }
                        Spacer()
                    }
                }
                .padding(.leading, screenWidth / 5)
                ,trailing: HStack {
                    NavigationSliderItem()
                    NavigationSearchItem()
                }
            )
            .navigationViewStyle(StackNavigationViewStyle())

enter image description here

if I insert NavigationView before ZStack this happens.

enter image description here

Navigation View is on Kayit ol page. You can see from the video

https://vimeo.com/567060877


Solution

  • I want to share a structure with you that will solve all of your woes. If I'm understanding you correctly, you have multiple navigation flows, eg. Login, Home, some other flow. Which is causing you to have NavigationView { Navigation View { NavigationView }}} nesting going on, with multiple back buttons.

    Here is a possible solution, and frankly it'll help a lot more in the future with other projects.

    Base View Model

    This view model is to control a Base View which is essentially a Navigation control view.

    class BaseViewModel: ObservableObject {
        @Published var userFlow: UserFlow = .loading
        
        init(){
             //You might check for login state here
             //IF logged in, then go to home, otherwise
             //go back to login. 
             userFlow = .home
        }
        
        enum UserFlow {
            case loading, onboarding, login, home
        }
    }
    

    Base View

    This BaseView will update whenever the BaseViewModel environment object is changed. It's bound, so when it changes, the user flow will change too. This will allow you to have multiple navigation stacks on a per-flow basis. In other words create one flow for login, another for logged-in, and any other for whatever you need, the navigation views will no longer interfere with each other.

    struct BaseView: View {
        //We use an @EnvironmentObject here because later on
        //in the app we access this and change the state
        //so that the BaseView updates it's flow. 
        @EnvironmentObject var appState: BaseViewModel
        
        var body: some View {
            //Make sure to group, or it won't work properly.
            Group {
                switch appState.userFlow {
                case .onboarding:
                    Text("Not Yet Implemented")
                case .login:
                    LandingPageView()
                case .home:
                    BaseHomeScreenView().environmentObject(BaseHomeScreenViewModel())
                case .loading:
                    LoadingView()
                }
            }
            .edgesIgnoringSafeArea(.bottom)
            .transition(.opacity)
            .animation(.spring())
        }
    }
    

    Usage

    Simply grab the created @EnvironmentObject and set its value. Swift will take over from there and swap your views using the switch appState.userFlow located in the BaseView

    struct View1: View {
        @EnvironmentObject var userFlow: BaseViewModel
        var body: some View {
            VStack {
                 Button(action: {userFlow = .login}, label: {
                      Text("Go to login")
                 })
            }
        }
    }
    

    Note I did this without my IDE, forgive any syntax errors.