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 NavigationLink
s 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 NavigationLink
s 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 NavigationLink
s 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:
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
Swift 2 Migration Savecontext() in Appdelegate
Can't Create a Range in Swift 3
Swift: How to Create External Interface for Static Library (Public Headers Analog in Objective-C .H)
In Swift, How to Get Memory Back to Normal After an Skscene Is Removed
How to Set Top Left and Right Corner Radius with Desired Drop Shadow in Uitabbar
Unwrapping Swift Optional Without Variable Reassignment
Add Instagram to Uiactivityviewcontroller
What's the Equivalent of Finally in Swift
Swift 3: Atomic_Compare_Exchange_Strong
How to Import a Swift Function Declared in a Compiled .Swiftmodule into Another Swift File
Getting Optional("") When Trying to Get Value from Keychain
Get Current Time as String Swift 3.0
Swiftui Exporting or Sharing Files
Exporting Mp4 Through Avassetexportsession Fails