I am new to SwiftUI (like most people) and trying to figure out how to remove some whitespace above a List that I embedded in a NavigationView
In this image, you can
View Modifiers made it easy:
//ViewModifiers.swift
struct HiddenNavigationBar: ViewModifier {
func body(content: Content) -> some View {
content
.navigationBarTitle("", displayMode: .inline)
.navigationBarHidden(true)
}
}
extension View {
func hiddenNavigationBarStyle() -> some View {
modifier( HiddenNavigationBar() )
}
}
Example:
import SwiftUI
struct MyView: View {
var body: some View {
NavigationView {
VStack {
Spacer()
HStack {
Spacer()
Text("Hello World!")
Spacer()
}
Spacer()
}
.padding()
.background(Color.green)
//remove the default Navigation Bar space:
.hiddenNavigationBarStyle()
}
}
}
There is a dedicated modifier to make the navigation bar take less space:
.navigationBarTitleDisplayMode(.inline)
There's no longer need to hide the navigation bar or set its title.
For me it was because I was pushing my NavigationView from an existing. In effect having one inside the other. If you are coming from a NavigationView you do not need to create one inside the next as you already inside a NavigatonView.
My solution for this problem was the same as suggested by @Genki and @Frankenstein.
I applied two modifiers to the inner list (NOT the NavigationView) to get rid of the spacing:
.navigationBarTitle("", displayMode: .automatic)
.navigationBarHidden(true)
On the outer NavigationView, then applied .navigationBarTitle("TITLE")
to set the title.
I also tried all the solutions mentioned on this page and only found @graycampbell solution the one to be working well, with well-working animations. So I tried to create a value I can just use throughout the app that I can access anywhere by the example of hackingwithswift.com
I created an ObservableObject
class
class NavBarPreferences: ObservableObject {
@Published var navBarIsHidden = true
}
And pass it on to the initial view in the SceneDelegate
like so
var navBarPreferences = NavBarPreferences()
window.rootViewController = UIHostingController(rootView: ContentView().environmentObject(navBarPreferences))
Then in the ContentView
we can keep track of this Observable object like so and create a link to SomeView
:
struct ContentView: View {
//This variable listens to the ObservableObject class
@EnvironmentObject var navBarPrefs: NavBarPreferences
var body: some View {
NavigationView {
NavigationLink (
destination: SomeView()) {
VStack{
Text("Hello first screen")
.multilineTextAlignment(.center)
.accentColor(.black)
}
}
.navigationBarTitle(Text(""),displayMode: .inline)
.navigationBarHidden(navBarPrefs.navBarIsHidden)
.onAppear{
self.navBarPrefs.navBarIsHidden = true
}
}
}
}
And then when accessing the second view (SomeView), we hide it again like this:
struct SomeView: View {
@EnvironmentObject var navBarPrefs: NavBarPreferences
var body: some View {
Text("Hello second screen")
.onAppear {
self.navBarPrefs.navBarIsHidden = false
}
}
}
To keep previews working add the NavBarPreferences to the preview like so:
struct SomeView_Previews: PreviewProvider {
static var previews: some View {
SomeView().environmentObject(NavBarPreferences())
}
}
Really loved the idea given by @Vatsal Manot To create a modifier for this.
Removing isHidden
property from his answer, as I don't find it useful as modifier name itself suggests that hide navigation bar.
// Hide navigation bar.
public struct NavigationBarHider: ViewModifier {
public func body(content: Content) -> some View {
content
.navigationBarTitle("")
.navigationBarHidden(true)
}
}
extension View {
public func hideNavigationBar() -> some View {
modifier(NavigationBarHider())
}
}