如何在SwiftUI NavigiationView中删除默认导航栏空间


82

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

在此图像中,您可以看到列表上方有一些空白

当前版本

我要完成的是

理想版本

我试过使用

.navigationBarHidden(true)

但这并没有进行任何明显的更改。

我目前正在这样设置我的navigationView

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

其中FileBrowserView是具有列表和像这样定义的单元格的视图

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

我确实要注意,这里的最终目标是您将能够单击这些单元格来更深入地导航到文件树,因此在更深层的导航栏中应该显示“返回”按钮,但是我不希望在在我的初始视图中排名最高。

Answers:


131

由于某些原因,SwiftUI要求您还设置.navigationBarTitle.navigationBarHidden可以正常工作。

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

更新资料

正如@Peacemoon在评论中指出的那样,无论您是否在后续视图中进行设置navigationBarHidden,当您在导航堆栈中进行更深入的浏览时,导航栏将保持隐藏false。就像我在评论中说的那样,这可能是由于Apple实施不佳或只是糟糕的文档(谁知道,也许有一种“正确”的方法来完成此操作)。

无论如何,我想出了一种解决方法,该解决方法似乎可以产生原始海报的预期效果。我很犹豫地推荐它,因为它看起来似乎不必要,但如果没有任何隐藏和取消隐藏导航栏的简单方法,这就是我能做的最好的事情。

本示例使用三个视图-View1具有隐藏的导航栏,View2并且View3两个视图均具有带有标题的可见导航栏。

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

设置navigationBarHiddenfalse导航堆栈上的更深层次的看法似乎并不恰当覆盖最初设置视图的偏好navigationBarHiddentrue,所以唯一的解决方法我能想出是使用绑定改变原来的观点的偏好时,一个新的视图被推到导航堆栈上。

就像我说的那样,这是一个很棘手的解决方案,但是如果没有Apple的官方解决方案,这就是我能想到的最好的解决方案。


5
这解决了我的问题!很奇怪,您必须先拥有标题才能隐藏导航栏...
Vapidant

5
该错误仍然存​​在于beta之外:/
Daniel Ryan

1
@Peacemoon我之前没有注意到。总而言之,感觉苹果的实施在这里很草率。您不必只设置标题以隐藏开始的栏,而在下一个视图上将设置navigationBarHiddenfalse则应取消隐藏导航栏,但事实并非如此。最终,我对SwiftUI的文档记录不怎么满意,回到了UIKit,至少有20个人来到这里只是为了学习如何隐藏导航栏,这对于Apple的实现和/或文档来说是很差的。抱歉,您没有更好的答案。
Graycampbell '19

2
@SambitPrakash我以前从未真正将TabView嵌套在NavigationView中,就我所知,Apple似乎并没有以这种方式将它们嵌套在其应用程序中。对我而言,是否完全打算将TabView嵌套在NavigationView中还不是很清楚,而且我知道SwiftUI嵌套嵌套时会弹出一些奇怪的错误。对我而言,TabViews一直感觉是比NavigationViews更高级的导航形式。如果您改为将NavigationView嵌套在TabView内,那么我认为我的解决方法应该仍然有效。
greycampbell '19

2
@kar令人失望的是,这个答案仍然受到关注和支持。我将其编写为对应该是临时错误的临时解决方案。我最近没有对其进行测试,但是显然有很多问题。还有些人问您是否可以在不使用NavigationView的情况下在视图之间导航。答案是肯定的,但实际上您必须从头开始编写自己的NavigationView。您不仅可以神奇地在视图之间导航。必须管理一些视图并在它们之间提供过渡,这就是我们拥有NavigationView的原因。
graycampbell

16

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

在此处输入图片说明

如果您不希望导航栏:

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文档


25
出于某些原因,您可能想要隐藏导航栏,同时又保持的功能NavigationView。a的目的NavigationView不仅仅是显示导航栏。
greycampbell

6
我想要NavigiationView具有在堆栈中导航并能够轻松从视图返回的功能,我不需要初始视图上的navigiationBar。
Vapidant

2
有没有一种方法可以在没有navigationView的情况下浏览视图?
user1445685 '19

基本上。号尚未对swiftui至少
古斯塔沃Parrado

该答案无济于事,因为拥有NavigationView会对原始问题产生影响,因为导航到另一个视图是必需的。
JaseTheAce

14

查看修饰符使操作变得简单:

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

例: 在此处输入图片说明

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

4
不能解决推送视图控制器的问题。
damjandd

这里的关键似乎是,修饰符未添加到NavigationView中,而是添加到了内部视图中。这使它开始工作变得很重要。谢谢!:-)
JaseTheAce

8

我还尝试了此页面上提到的所有解决方案,但仅发现@graycampbell解决方案可以很好地工作,并且动画效果良好。因此,我尝试创建一个可以在整个应用程序中使用的值,并可以通过hackingwithswift.com的示例在任何地方访问

我创建了一个ObservableObject

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

然后SceneDelegate像这样传递给初始视图

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

然后,ContentView我们可以像这样跟踪该Observable对象,并创建一个指向的链接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())
    }
}

1
使用@EnvironmentObject更好地在整个应用程序中传递数据,而不是@State,所以我希望您回答更多
Arafin Russell

6

这是SwiftUI中存在的错误(从Xcode 11.2.1开始)。我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())
    }
}

2
这样,“后退”手势将不再起作用
Urkman

5

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

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

然后只需调用例如:

ZStack {
    *YOUR CONTENT*
}
.hideNavigationBar()

5

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

.navigationBarTitle("", displayMode: .inline)

开始发行 解决方案1 然后只需更改导航栏的外观

init() {
    UINavigationBar.appearance().setBackgroundImage(UIImage(), for: .default)
    UINavigationBar.appearance().shadowImage = UIImage()
}

在拥有初始NavigationView的视图上。 最终解决方案

如果要在屏幕之间更改外观,请在适当的视图中更改外观


此解决方案很有用
Murilo Medeiros

4

对于我来说,我申请了.navigationBarTitleNavigationView,而不是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)
        }
    }
}

导航栏和列表顶部无间隙


5
与提出的问题无关
Ahmed Sahib

3
@AhmedSahib问题是“如何在SwiftUI NavigiationView中删除默认导航栏空间”,我的代码完成了这一工作。
Genki

1
极好的建议。对于我的解决方案,我必须对内部列表应用两个修饰符以消除间距:.navigationBarTitle(“”,displayMode:.automatic).navigationBarHidden(true)然后,在外部NavigationView上,我必须应用:.navigationBarTitle(“ TITLE”,displayMode:.inline)
科学怪人

2

与@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
                }
        }
    }
}

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


2

对我来说,这是因为我正在从现有版本中推送我的NavigationView。实际上是一个在另一个内部。如果您来自NavigationView,则不需要像在NavigatonView中那样在下一个中创建一个。


0

真的很喜欢@Vatsal Manot给出的想法,为此要创建一个修改器。从他的答案中
删除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())
    }
}

0

在使用应用户登录后应显示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")
            }
        }
    }
}

0

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

我对内部列表应用了两个修饰符(而不是NavigationView)以摆脱间距:

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

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


1
它什么也没做。
TruMan1

0

SwiftUI 2

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

.navigationBarTitleDisplayMode(.inline)

不再需要隐藏导航栏或设置其标题。


-7

尝试把NavigationView里面放进去GeometryReader

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

NavigationView以根视图显示时,我经历了奇怪的行为。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.