如何删除SwiftUI NavigationView中的默认导航栏空间

我是SwiftUI的新手(像大多数人一样),并试图弄清楚如何删除我嵌入在NavigationView中的List上的一些空白

在此图像中,您可以看到List上方有一些空白区域。

Current Version

我想要完成的是:

Ideal Version

我试过使用:

.navigationBarHidden(true)

但这并没有带来任何明显的变化。

我目前正在设置我的NavigiationView,如下所示:

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

其中,FileBrowserView是具有ListFileCellS的视图,其定义如下:

List {
Section(header: Text("Root")) {
FileCell(name: "Test", fileType: "JPG",fileDesc: "Test number 1")
FileCell(name: "Test 2", fileType: "txt",fileDesc: "Test number 2")
FileCell(name: "test3", fileType: "fasta", fileDesc: "")
}
}

我想指出的是,这里的最终目标是,您将能够单击这些单元格以更深入地导航到文件树中,因此应该在更深入的导航栏上显示一个后退按钮,但在我的初始视图中,我不希望在顶部显示任何内容。

116311 次浏览

NavigationView的目的是将导航栏添加到视图的顶部。在IOS中,有两种导航栏:大导航栏和标准导航栏。

enter image description here

如果您不想要导航栏:

FileBrowserView(jsonFromCall: URLRetrieve(URLtoFetch: applicationDelegate.apiURL))

如果需要大导航栏(通常用于顶级视图):

NavigationView {
FileBrowserView(jsonFromCall: URLRetrieve(URLtoFetch: applicationDelegate.apiURL))
.navigationBarTitle(Text("Title"))
}

如果需要标准(内嵌)导航栏(通常用于子级视图):

NavigationView {
FileBrowserView(jsonFromCall: URLRetrieve(URLtoFetch: applicationDelegate.apiURL))
.navigationBarTitle(Text("Title"), displayMode: .inline)
}

希望这个答案对你有帮助。

更多信息:Apple文档

出于某种原因,SWIFTUI还要求您设置.navigationBarTitle,以使.navigationBarHidden正常工作。

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

更新

正如@PeaceMoon在评论中指出的那样,无论您是否在后续视图中将navigationBarHidden设置为false,当您在导航堆栈中进行更深的导航时,导航栏都会保持隐藏状态。正如我在评论中所说的,这要么是苹果公司执行不力的结果,要么就是糟糕的文档(谁知道呢,也许有一种“正确”的方法来实现这一点)。

无论如何,我想出了一个变通办法,似乎产生了最初发帖人想要的结果。我很犹豫要不要推荐它,因为它看起来毫无必要,但没有任何直接的方法来隐藏和取消隐藏导航栏,这是我能做的最好的。

此示例使用三个视图-View1具有隐藏的导航栏,View2View3都具有带标题的可见导航栏。

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")
}
}

在导航堆栈中更深的视图上将navigationBarHidden设置为false似乎不能正确覆盖最初将navigationBarHidden设置为true的视图的首选项,因此我能想到的唯一解决方法是在将新视图推送到导航堆栈上时使用绑定来更改原始视图的首选项。

就像我说的,这是一个蹩脚的解决方案,但没有苹果的官方解决方案,这是我能想到的最好的解决方案。

尝试将NavigationView放入GeometryReader中。

GeometryReader {
NavigationView {
Text("Hello World!")
}
}

NavigationView是根视图时,我遇到过奇怪的行为。

这是SwiftUI(__Xcode 11.2.1的ABC1)中存在的错误。我写了一个ViewModifier来解决这个问题,基于现有答案的代码:

public struct NavigationBarHider: ViewModifier {
@State var isHidden: Bool = false


public func body(content: Content) -> some View {
content
.navigationBarTitle("")
.navigationBarHidden(isHidden)
.onAppear { self.isHidden = true }
}
}


extension View {
public func hideNavigationBar() -> some View {
modifier(NavigationBarHider())
}
}

与@GrayCampbell的回答类似,但更简单一些:

struct YourView: View {


@State private var isNavigationBarHidden = true


var body: some View {
NavigationView {
VStack {
Text("This is the master view")
NavigationLink("Details", destination: Text("These are the details"))
}
.navigationBarHidden(isNavigationBarHidden)
.navigationBarTitle("Master")
.onAppear {
self.isNavigationBarHidden = true
}
.onDisappear {
self.isNavigationBarHidden = false
}
}
}
}

设置标题是必要的,因为它显示在您导航到的视图中的“后退”按钮旁边。

您可以像这样扩展本机查看协议:

extension View {
func hideNavigationBar() -> some View {
self
.navigationBarTitle("", displayMode: .inline)
.navigationBarHidden(true)
}
}

然后只需调用例如:

ZStack {
*YOUR CONTENT*
}
.hideNavigationBar()

对我来说,我将.navigationBarTitle应用于NavigationView,而不是List是罪魁祸首。这在Xcode 11.2.1上对我有用:

struct ContentView: View {
var body: some View {
NavigationView {
List {
NavigationLink(destination: DetailView()) {
Text("I'm a cell")
}
}.navigationBarTitle("Title", displayMode: .inline)
}
}
}

Navigation bar and list with no gap at the top

对我来说,这是因为我把我的导航视图从一个现有的。实际上是一个在另一个里面。如果您来自NavigationView,则不需要在下一个中创建一个,因为您已经在NavigationView中。

真的很喜欢@瓦萨尔·马诺特给出的想法,为此创建一个修改器。
从他的答案中删除了isHidden属性,因为我不觉得它有用,因为修饰符名称本身暗示隐藏导航栏。

// 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())
}
}

视图修改器使其变得简单:

//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() )
}
}

示例: enter image description here

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()
}
}
}

我还尝试了这个页面上提到的所有解决方案,只发现@GrayCampbell解决方案运行良好,动画效果良好。因此,我尝试创建一个可以在整个应用程序中使用的值,我可以通过Hackingwithswift.com的示例在任何地方访问它。

我创建了一个ObservableObject类。

class NavBarPreferences: ObservableObject {
@Published var navBarIsHidden = true
}

并将其传递给SceneDelegate中的初始视图,如下所示

var navBarPreferences = NavBarPreferences()
window.rootViewController = UIHostingController(rootView: ContentView().environmentObject(navBarPreferences))

然后,在ContentView中,我们可以像这样跟踪这个可观察对象,并创建到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
}
}
}
}

然后,当访问第二个视图(SomeView)时,我们再次将其隐藏,如下所示:

struct SomeView: View {
@EnvironmentObject var navBarPrefs: NavBarPreferences


var body: some View {
Text("Hello second screen")
.onAppear {
self.navBarPrefs.navBarIsHidden = false
}
}
}

要保持预览正常工作,请将NavBarPreferences添加到预览中,如下所示:

struct SomeView_Previews: PreviewProvider {
static var previews: some View {
SomeView().environmentObject(NavBarPreferences())
}
}

我在一个应用程序上工作时遇到了类似的问题,一旦用户登录,就应该显示TabView.

正如@GrayCampbell在他的评论中建议的那样,TabView不应该嵌入到NavigationView中,否则就会出现“空白”。即使在使用.navigationBarHidden(true)时也会出现

我使用了一个ZStack来隐藏NavigationView.请注意,对于这个简单的示例,我使用@State@Binding来管理UI可见性,但是您可能希望使用更复杂的对象,如环境对象。

struct ContentView: View {


@State var isHidden = false


var body: some View {
        

ZStack {
if isHidden {
DetailView(isHidden: self.$isHidden)
} else {
NavigationView {
Button("Log in"){
self.isHidden.toggle()
}
.navigationBarTitle("Login Page")
}
}
}
}
}

当我们按下登录按钮时,初始页面消失,并且加载了DetailView.当我们切换“注销”按钮时,登录页面重新出现

struct DetailView: View {
    

@Binding var isHidden: Bool
    

var body: some View {
TabView{
NavigationView {
Button("Log out"){
self.isHidden.toggle()
}
.navigationBarTitle("Home")
}
.tabItem {
Image(systemName: "star")
Text("One")
}
}
}
}

如果您将要删除空间的视图的标题设置为内联,则不需要在具有NavigationView的视图上执行此操作,但也需要在导航的视图上执行此操作。

.navigationBarTitle("", displayMode: .inline)

Starting issue solution 1 然后只需更改导航栏的外观

init() {
UINavigationBar.appearance().setBackgroundImage(UIImage(), for: .default)
UINavigationBar.appearance().shadowImage = UIImage()
}
在保存初始NavigationView

的视图上。 final solution

如果要在屏幕之间更改外观,请在相应的视图中更改外观。

我对这个问题的解决方案与@Genki和@Frankenstein的建议相同。

我对内部列表(不是NavigationView)应用了两个修饰符来消除间距:

.navigationBarTitle("", displayMode: .automatic)
.navigationBarHidden(true)

在外部NavigationView上,然后应用.navigationBarTitle("TITLE")来设置标题。

IOS 14+

有一个专用的修改器可以使导航栏占用更少的空间:

.navigationBarTitleDisplayMode(.inline)

编辑

在某些情况下,可能仍然需要添加.navigationBarHidden(true)

我为此挣扎了一段时间,但最终对我起作用的是..

ZStack {
...
}
.edgesIgnoringSafeArea(.all) //or .edgesIgnoringSafeArea(.top)
.navigationBarBackButtonHidden(true)
.navigationBarHidden(true)

我试着设置 .navigationBarTitle("", displayMode: .inline) .navigationBarHidden(true) 但它不起作用。问题是我把它设置为

NavigationView{...}.navigationBarTitle("", displayMode: .inline)
.navigationBarHidden(true)

但要去掉NagigationBar,应该将其设置为内部视图。

NavigationView{
InnerView{}.navigationBarTitle("", displayMode: .inline)
.navigationBarHidden(true)
}

希望这能有所帮助 要了解实际情况,您可以查看这个开源应用程序(WIP)https://github.com/deepaksingh4/kidsbookapp

把下面的代码放在你的NextView上

        .navigationBarBackButtonHidden(true)
.navigationBarHidden(true)

但是,当通过NavigationLink推送到NextView时,您还必须像这样放置修饰符:

        NavigationLink(
destination: NextView()
.navigationBarTitle("")
.navigationBarHidden(true)
) {
Text("NEXT VIEW")
}
                    

我尝试在vStack的大括号末尾添加.NavigationBarHidden(true),如下所示

NavigationView { Vstack(){"some Code"}.navigationBarHidden(true)}

导航栏enter image description here消失,但如果我在导航栏的大括号末尾添加.navigationBarHidden(true),如下所示

    NavigationView { Vstack(){"some Code"}}.navigationBarHidden(true)

enter image description here,导航栏不会消失

同样的问题,我终于解决了。要使导航完全消失,您需要将这些修饰符添加到NavigationView及其内部的所有NavigationsLinks

.navigationBarHidden(true)
.navigationBarTitleDisplayMode(.inline)

如果你不这样做,NavigationLinks也不会起作用。

我必须从屏幕1导航到屏幕2。如果我将其用于上面的导航视图,答案导航栏将被隐藏,但其空间仍然存在(高度的空间量)在屏幕1中。

最后,我有自己的解决方案,在NavigationView中的任何视图中使用此代码,而不关心NavigationBarTitle.就像这样:

屏幕1:

NavigationView {
SomeView {
NavigationLink {
// go to screen2
}
}.navigationBarHidden(true)
}

屏幕2:

NavigationView {
// some Views
}.navigationBarHidden(true)

你不需要设置标题,你可以简单地使用.stack

NavigationView {
VStack {
Color.cyan
}
.navigationBarHidden(true)
}
.navigationViewStyle(.stack)  // Here

我有同样的问题,并发现下面的代码工作得最好。

   .navigationTitle("")
.navigationBarBackButtonHidden(true)

我知道我在这里有点晚了,但我刚刚使用这里的顶部答案解决了这个问题: 如何使用SwiftUI消除嵌套NavigationView中的空间

如果该页面的内容发生变化,我将在下面解释答案。

仅在任何需要导航的视图的最顶层使用NavigationView包装器,无论嵌套的子视图向下延伸多远。它们都已经具有NavigationView属性,您可以随时在子视图中调用NavigationLink.我在子视图周围有很多额外的NavigationView包装器,删除它们可以删除额外的空白,同时保留所有导航链接的功能。

这是迄今为止我发现的最简单、最稳定的方法。您可以通过隐藏整个工具栏来隐藏导航标题和“后退”按钮。您还可以选择在您希望的任何视图中显示它。您可以使用.toolbar(.hidden)来隐藏它,并使用.toolbar(.visible)修改器使其可见。

IOS 16+

struct ContentView: View {
var body: some View {
NavigationStack {
List {
ForEach(0..<10) { i in
NavigationLink {
Text("Detail for Row \(i)")
} label: {
Text("Row \(i)")
}
}
}
            

.toolbar(.hidden)
}
}
}

如果您的目标是低于IOS 16,则可以将NavigationStack替换为NavigationView

尝试将属性(导航标题、工具栏等)放在导航视图之外。像这样:

NavigationView {
}
.navigationTitle("Detail News")
.toolbarColorScheme(.dark, for: .navigationBar)
.toolbarBackground(Color.gray, for: .navigationBar)
.toolbarBackground(.visible, for: .navigationBar)
.accentColor(.white)