How to Avoid Nested Navigation Bars in Swiftui

How can I avoid nested Navigation Bars in SwiftUI?

You should only have one NavigationView in your view hierarchy, as an ancestor of the menu view. You can then use NavigationLinks at any level of the hierarchy under that.

So, for example, your root view could be defined like this:

struct RootView: View {
var body: some View {
NavigationView {
MenuView()
.navigationBarItems(trailing: profileButton)
}
}

private var profileButton: some View {
Button(action: { }) {
Image(systemName: "person.crop.circle")
}
}
}

Then your menu view has NavigationLinks to the appropriate views:

struct MenuView: View {
var body: some View {
List {
link(icon: "calendar", label: "Appointments", destination: AppointmentListView())
link(icon: "list.bullet", label: "Work Order List", destination: WorkOrderListView())
link(icon: "rectangle.stack.person.crop", label: "Contacts", destination: ContactListView())
link(icon: "calendar", label: "My Calendar", destination: MyCalendarView())
}.navigationBarTitle(Text("Menu"), displayMode: .large)
}

private func link<Destination: View>(icon: String, label: String, destination: Destination) -> some View {
return NavigationLink(destination: destination) {
HStack {
Image(systemName: icon)
Text(label)
}
}
}
}

Your appointment list view also contains NavigationLinks to the appointment detail views:

struct AppointmentListView: View {
var body: some View {
List {
link(destination: AppointmentDetailView())
link(destination: AppointmentDetailView())
link(destination: AppointmentDetailView())
}.navigationBarTitle("Appointments")
}

private func link<Destination: View>(destination: Destination) -> some View {
NavigationLink(destination: destination) {
AppointmentView()
}
}
}

Result:

demo

Swift UI with Nested Navigation Views

As stated earlier in the comments you need to remove a NavigationView. And almost always you'll want to remove any NavigationView on the children views.

Essentially what's happening is you are double stacking your NavViews and can cause some really funky behavior.

Read more on NavigationView

Hiding Navigation Bar in case of multiple Navigation Views in SwiftUI

If you want to hide navigation bar completely at third view here is possible approach. (Note: btw in one view hierarchy there must be only one NavigationView, so another one in ThirdView is not needed)

Tested with Xcode 11.4 / iOS 13.4

class HideBarViewModel: ObservableObject {
@Published var isHidden = false
}

struct FirstView: View {
@ObservedObject var vm = HideBarViewModel()
init() {
UINavigationBar.appearance().backgroundColor = UIColor.green
}

var body: some View {
NavigationView {
NavigationLink(destination: SecondView()) {
Text("Second View")
}.navigationBarTitle("First View")
.navigationBarHidden(vm.isHidden)
}.environmentObject(vm)
}
}

// Second View

struct SecondView: View {
var body: some View {
NavigationLink(destination: ThirdView()) {
Text("Third View")
}
}
}

// Third View

struct ThirdView: View {
@EnvironmentObject var vm: HideBarViewModel
var body: some View {
Text("Welcome")
.onAppear {
self.vm.isHidden = true
}
}
}

How to remove the default Navigation Bar space in SwiftUI NavigationView

For some reason, SwiftUI requires that you also set .navigationBarTitle for .navigationBarHidden to work properly.

NavigationView {
FileBrowserView(jsonFromCall: URLRetrieve(URLtoFetch: applicationDelegate.apiURL))
.navigationBarTitle("")
.navigationBarHidden(true)
}

Update

As @Peacemoon pointed out in the comments, the navigation bar remains hidden as you navigate deeper in the navigation stack, regardless of whether or not you set navigationBarHidden to false in subsequent views. As I said in the comments, this is either a result of poor implementation on Apple's part or just dreadful documentation (who knows, maybe there is a "correct" way to accomplish this).

Whatever the case, I came up with a workaround that seems to produce the original poster's desired results. I'm hesitant to recommend it because it seems unnecessarily hacky, but without any straightforward way of hiding and unhiding the navigation bar, this is the best I could do.

This example uses three views - View1 has a hidden navigation bar, and View2 and View3 both have visible navigation bars with titles.

struct View1: View {
@State var isNavigationBarHidden: Bool = true

var body: some View {
NavigationView {
ZStack {
Color.red
NavigationLink("View 2", destination: View2(isNavigationBarHidden: self.$isNavigationBarHidden))
}
.navigationBarTitle("Hidden Title")
.navigationBarHidden(self.isNavigationBarHidden)
.onAppear {
self.isNavigationBarHidden = true
}
}
}
}

struct View2: View {
@Binding var isNavigationBarHidden: Bool

var body: some View {
ZStack {
Color.green
NavigationLink("View 3", destination: View3())
}
.navigationBarTitle("Visible Title 1")
.onAppear {
self.isNavigationBarHidden = false
}
}
}

struct View3: View {
var body: some View {
Color.blue
.navigationBarTitle("Visible Title 2")
}
}

Setting navigationBarHidden to false on views deeper in the navigation stack doesn't seem to properly override the preference of the view that originally set navigationBarHidden to true, so the only workaround I could come up with was using a binding to change the preference of the original view when a new view is pushed onto the navigation stack.

Like I said, this is a hacky solution, but without an official solution from Apple, this is the best that I've been able to come up with.

How to get rid of space in nested NavigationView with SwiftUI

Only use NavigationView at the top level, you don't need to add it in every subscreen, just remove it from CalendarList and DateDetails and it will fix your spacing issue

SwiftUI - Two navigation bars

You are adding second NavigationView in RegisterView. That is the issue! because of using ContentView, for solving use MainView().

 struct RegisterView: View {
@ObservedObject var registerController = RegisterController()

@State private var showingRegistrationAlert = false

@State var navigateNext = false

let lightGreyColor = Color(red: 239.0/255.0, green: 243.0/255.0, blue: 244.0/255.0, opacity: 1.0)

var body: some View {
VStack {
VStack(alignment: .leading) {

Spacer()

NavigationLink(destination: MainView(), isActive:$navigateNext) { // <<: here
Text("")
}
Button(action: { if registerController.validateRegistration() {
showingRegistrationAlert.toggle()
}}) {
Text("Enviar registro")
}
.alert(isPresented: $showingRegistrationAlert) {
Alert(title: Text("Se registro correctamente"), dismissButton: .default(Text("Ok"), action: {
navigateNext.toggle()
}))

}
.padding()
.navigationBarTitle(Text("Registracion"), displayMode: .inline)

}
}


Related Topics



Leave a reply



Submit