【问题标题】:How to remove the default Navigation Bar space in SwiftUI NavigationView如何删除 SwiftUI NavigationView 中的默认导航栏空间
【发布时间】:2019-12-22 09:00:03
【问题描述】:

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

在这张图片中,你可以看到List上方有一些空白。

我想要完成的是:

我尝试过使用:

.navigationBarHidden(true)

但这并没有产生任何明显的变化。

我目前正在像这样设置我的导航视图:

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

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

【问题讨论】:

    标签: ios swift swiftui swiftui-list swiftui-navigationview


    【解决方案1】:

    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 Documentation

    【讨论】:

    • 您可能希望隐藏导航栏同时保持NavigationView 的功能是有原因的。 NavigationView 的目的不仅仅是显示导航栏。
    • 我希望 NavigiationView 具有在堆栈中导航并能够轻松从视图返回的功能,我不需要初始视图上的导航栏。
    • 有没有不用navigationView的视图导航?
    • 基本上。不。至少在 swiftui 上还没有
    • 这个答案没有帮助,因为拥有 NavigationView 会影响原始问题,因为它需要导航到另一个视图。
    【解决方案2】:

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

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

    更新

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

    无论如何,我想出了一个解决方法,似乎可以产生原始海报的预期结果。我不太愿意推荐它,因为它看起来不必要的 hacky,但是没有任何直接的隐藏和取消隐藏导航栏的方法,这是我能做的最好的。

    这个例子使用了三个视图——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")
        }
    }
    

    在导航堆栈更深的视图上设置 navigationBarHiddenfalse 似乎没有正确覆盖最初将 navigationBarHidden 设置为 true 的视图的偏好,所以我能想出的唯一解决方法当新视图被推送到导航堆栈时,正在使用绑定来更改原始视图的首选项。

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

    【讨论】:

    • 这解决了我的问题!很奇怪,必须要有标题才能隐藏导航栏...
    • 这个 bug 在 beta 之外仍然存在:/
    • @Peacemoon 我之前没有注意到这一点。总而言之,感觉苹果的实现在这里相当草率。您不必设置标题只是为了隐藏栏开始,并且在下一个视图上将navigationBarHidden 设置为false 应该取消隐藏导航栏,但事实并非如此。我最终厌倦了 SwiftUI 的文档记录如此糟糕并回到 UIKit,而且至少有 20 人来到这里只是为了学习如何隐藏导航栏这一事实对于 Apple 的实现和/或文档来说非常糟糕。对不起,我没有更好的答案给你。
    • @SambitPrakash 我以前从未真正将 TabView 嵌套在 NavigationView 中,据我所知,Apple 似乎也没有将它们嵌套在他们的应用程序中。我从来没有完全清楚是否打算在 NavigationView 中嵌套 TabView,而且我知道 SwiftUI 有一些奇怪的错误,当你以这种方式嵌套它们时会弹出一些奇怪的错误。在我看来,TabViews 总是比 NavigationViews 更高级的导航形式。如果您将 NavigationView 嵌套在 TabView 中,我相信我的解决方法应该仍然有效。
    • @kar 令人失望的是,这个答案仍然受到关注和支持。我写它是为了解决应该是临时错误的临时解决方案。我最近没有测试过它,但显然它有很多问题。一些人还询问是否可以在不使用 NavigationView 的情况下在视图之间导航。答案是肯定的,但您基本上必须从头开始编写自己的 NavigationView。您不能只是神奇地在视图之间导航。必须有一些东西来管理这些视图并提供它们之间的转换,这就是我们拥有 NavigationView 的原因。
    【解决方案3】:

    尝试将NavigationView 放入GeometryReader

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

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

    【讨论】:

      【解决方案4】:

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

      【讨论】:

      • 这样,“快速后退”手势不再起作用
      • 我还需要添加.onDisappear { self.isHidden = false } 以使导航栏出现在子视图中。此外,向后滑动手势似乎也可以正常工作。
      【解决方案5】:

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

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

      【讨论】:

        【解决方案6】:

        您可以像这样扩展原生 View 协议:

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

        然后只需调用例如:

        ZStack {
            *YOUR CONTENT*
        }
        .hideNavigationBar()
        

        【讨论】:

          【解决方案7】:

          对我来说,我将.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)
                  }
              }
          }
          

          【讨论】:

          • @AhmedSahib 问题是“如何删除 SwiftUI NavigiationView 中的默认导航栏空间”,我的代码实现了这一点。
          • 很好的建议。对于我的解决方案,我必须对内部列表应用两个修饰符以消除间距: .navigationBarTitle("", displayMode: .automatic) .navigationBarHidden(true) 然后在外部 NavigationView 我必须应用:.navigationBarTitle("标题",显示模式:.inline)
          • 谢谢兄弟。我也遇到了同样的问题。
          【解决方案8】:

          对我来说,这是因为我正在从现有的 NavigationView 推送。实际上有一个在另一个里面。如果您来自 NavigationView,则无需在下一个中创建一个,因为您已经在 NavigatonView 中。

          【讨论】:

            【解决方案9】:

            真的很喜欢 @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())
                }
            }
            

            【讨论】:

              【解决方案10】:

              视图修饰符让一切变得简单:

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

              【讨论】:

              • 不能解决推送视图控制器的问题。
              • 这里似乎很关键,修饰符没有添加到 NavigationView 中,而是添加到里面的视图中。这使得让它工作变得与众不同。谢谢! :-)
              【解决方案11】:

              我还尝试了此页面上提到的所有解决方案,只发现 @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())
                  }
              }
              

              【讨论】:

              • 使用 @EnvironmentObject@State 在整个应用程序中传递数据要好得多,所以我更希望你回答更多
              【解决方案12】:

              在用户登录后应显示 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 中很有意义,但是一旦完成,我想显示一个 TabView。当切换到 TabView 时,我使用了这个答案和 EnvironmentObject 信号的组合。我不想通过所有多重导航传递状态/绑定,所以 EnvironmentObject 更有意义。
              【解决方案13】:

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

              .navigationBarTitle("", displayMode: .inline)
              

              然后只需更改导航栏的外观

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

              在包含初始 NavigationView 的视图上。

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

              【讨论】:

              • 这个解决方案很有用
              【解决方案14】:

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

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

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

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

              【讨论】:

              • 它什么也没做。
              【解决方案15】:

              SwiftUI 2

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

              .navigationBarTitleDisplayMode(.inline)
              

              编辑

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

              【讨论】:

              • 这只是让导航栏变小。仍然需要完全删除它。
              • 这是一个有用的解决方案,可以最大限度地减少占用的空间,谢谢!
              【解决方案16】:

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

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

              【讨论】:

                【解决方案17】:

                我尝试设置 .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

                【讨论】:

                  【解决方案18】:

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

                          .navigationBarBackButtonHidden(true)
                          .navigationBarHidden(true)
                  

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

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

                  【讨论】:

                    【解决方案19】:

                    我尝试像这样在我的 Vstack 的大括号末尾添加 .navigationBarHidden(true)

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

                    导航栏消失 但如果我像这样在导航栏的大括号末尾添加 .navigationBarHidden(true)

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

                    导航栏不会消失

                    【讨论】:

                      【解决方案20】:

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

                      .navigationBarHidden(true)
                      .navigationBarTitleDisplayMode(.inline)
                      

                      如果您不这样做,NavigationLinks 也将不起作用。

                      【讨论】:

                        【解决方案21】:

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

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

                        屏幕1:

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

                        屏幕2:

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

                        【讨论】:

                          猜你喜欢
                          • 1970-01-01
                          • 1970-01-01
                          • 2020-10-30
                          • 1970-01-01
                          • 1970-01-01
                          • 2020-11-06
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          相关资源
                          最近更新 更多