簡體   English   中英

SwiftUI NavigationView中如何去除默認Navigation Bar空間

[英]How to remove the default Navigation Bar space in SwiftUI NavigationView

我是 SwiftUI 的新手(像大多數人一樣)並試圖弄清楚如何刪除我嵌入NavigationViewList上方的一些空格

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

當前版本

我想要完成的是:

理想版本

我試過使用:

.navigationBarHidden(true)

但這並沒有做出任何明顯的改變。

我目前正在這樣設置我的 navigiationView:

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

其中FileBrowserView是一個帶有ListFileCell的視圖,定義如下:

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

我確實想指出,這里的最終目標是您將能夠單擊這些單元格以更深入地導航到文件樹中,因此應該在更深入的導航欄上顯示一個后退按鈕,但我不想要任何東西在我最初的看法中,這樣的頂部。

出於某種原因,SwiftUI 要求您還為.navigationBarHidden設置.navigationBarTitle才能正常工作。

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

更新

正如@Peacemoon 在評論中指出的那樣,當您在導航堆棧中更深入地導航時,導航欄將保持隱藏狀態,無論您是否在后續視圖中將navigationBarHidden設置為false 正如我在評論中所說,這要么是 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")
    }
}

在導航堆棧更深的視圖上將navigationBarHidden設置為false似乎無法正確覆蓋最初將navigationBarHidden設置為true的視圖的偏好,所以我能想出的唯一解決方法是使用綁定來更改原始偏好將新視圖推送到導航堆棧時的視圖。

就像我說的,這是一個 hacky 解決方案,但沒有 Apple 的官方解決方案,這是我能想到的最好的解決方案。

視圖修改器使它變得容易:

//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的目的是在視圖頂部添加導航欄。 在 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 文檔

SwiftUI 2

有一個專門的修飾符可以使導航欄占用更少的空間:

.navigationBarTitleDisplayMode(.inline)

編輯

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

如果將標題設置為要刪除空間的視圖的內聯,則不需要在具有 NavigationView 的視圖上執行此操作,但導航視圖也需要。

.navigationBarTitle("", displayMode: .inline)

開始問題 解決方案 1 然后只需更改導航欄的外觀

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

在包含初始 NavigationView 的視圖上。 最終解決方案

如果要在屏幕之間更改外觀,請在相應視圖中更改外觀

對我來說,我將.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)
        }
    }
}

頂部沒有間隙的導航欄和列表

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

我還嘗試了此頁面上提到的所有解決方案,只發現 @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())
    }
}

將以下代碼放在您的 NextView 上

        .navigationBarBackButtonHidden(true)
        .navigationBarHidden(true)

但是在通過 NavigationLink 推送到 NextView 時,您還必須像這樣放置修飾符:

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

您可以像這樣擴展本機View協議:

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

然后只需調用例如:

ZStack {
    *YOUR CONTENT*
}
.hideNavigationBar()

對我來說,這是因為我正在從現有的 NavigationView 推送。 實際上有一個在另一個里面。 如果您來自 NavigationView,則不需要在下一個中創建一個,因為您已經在 NavigatonView 中。

你不需要設置標題,你可以簡單地使用.stack

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

我對這個問題的解決方案與@Genki 和@Frankenstein 建議的相同。

我對內部列表(不是 NavigationView)應用了兩個修飾符以消除間距:

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

在外部 NavigationView 上,然后應用.navigationBarTitle("TITLE")來設置標題。

同樣的問題,我終於解決了。 為了讓導航完全消失,您需要將這些修飾符添加到NavigationView和其中的所有NavigationsLinks中:

.navigationBarHidden(true)
.navigationBarTitleDisplayMode(.inline)

如果您不這樣做, NavigationLinks也將不起作用。

類似於@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
                }
        }
    }
}

設置標題是必要的,因為它顯示在您導航到的視圖中的后退按鈕旁邊。

我嘗試設置.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

我嘗試像這樣在我的 Vstack 的大括號末尾添加 .navigationBarHidden(true)

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

導航欄消失在此處輸入圖像描述 但是如果我像這樣在導航欄的大括號末尾添加 .navigationBarHidden(true)

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

導航欄不會消失在此處輸入圖像描述

在用戶登錄后應顯示 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")
                }
            }
        }
    }
}

當我們按下 Log In 按鈕時,初始頁面消失,並加載了 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")
            }
        }
    }
}

我為此苦苦掙扎了一段時間,但最終對我有用的是......

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

我必須將 screen1 導航到 screen2。 如果我將其用於 NavigationView,如上面的答案導航欄將被隱藏,但它的空間仍然存在(屏幕 1 中的空間量與高度)。

最后,我有自己的解決方案,可以在 NavigationView 內的任何視圖中使用此代碼,並且不關心 navigationBarTitle。 像這樣:

屏幕1:

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

屏幕2:

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

我遇到了同樣的問題,發現下面的代碼效果最好。

   .navigationTitle("")
   .navigationBarBackButtonHidden(true)

我知道我在這里有點晚了,但我只是使用這里的最佳答案解決了這個問題: How to get rid of space in nested NavigationView with SwiftUI

如果該頁面的內容發生變化,我將在下面解釋答案。

僅在需要導航的任何視圖的最頂層使用 NavigationView 包裝器,無論嵌套子級下降多遠。 它們都已經具有 NavigationView 屬性,您可以隨時在子視圖中調用 NavigationLink。 我在子視圖周圍有很多額外的 NavigationView 包裝器,刪除它們刪除了額外的空白空間,同時保留了所有導航鏈接的功能。

這是迄今為止我發現的最簡單、最穩定的方法。 您可以通過隱藏整個工具欄來隱藏導航標題和后退按鈕。 您可以顯示也可以選擇以您希望的任何視圖顯示它。 您可以使用.toolbar(.hidden) .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)

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

嘗試將NavigationView放在GeometryReader中。

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

NavigationView是根視圖時,我遇到了奇怪的行為。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM