Как удалить пространство панели навигации по умолчанию в SwiftUI NavigiationView

Я новичок в SwiftUI (как и большинство людей) и пытаюсь выяснить, как удалить некоторые пробелы над списком, который я встроил в NavigationView

На этом изображении видно, что над списком есть пробел

Текущая версия

Что я хочу сделать, это

Идеальная версия

Я пытался использовать

.navigationBarHidden(true)

но это не внесло заметных изменений.

в настоящее время я настраиваю навигацию

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

Я хочу отметить, что конечной целью здесь является то, что вы сможете нажимать на эти ячейки для более глубокой навигации по дереву файлов и, следовательно, должны отображать кнопку "Назад" на панели при более глубокой навигации, но я не хочу, чтобы что-то сверху как таковой во время моего первоначального просмотра.

26 ответов

Решение

По какой-то причине SwiftUI требует, чтобы вы также установили .navigationBarTitle за .navigationBarHidden работать правильно.

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

Модификаторы просмотра упростили задачу:

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

SwiftUI 2

Есть специальный модификатор, чтобы панель навигации занимала меньше места:

.navigationBarTitleDisplayMode(.inline)

Больше не нужно скрывать панель навигации или устанавливать ее заголовок.

Цель NavigationView это добавить панель навигации в верхней части вашего представления. В iOS есть 2 вида навигационных панелей: большая и стандартная.

Если вы не хотите использовать панель навигации:

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

Если вы устанавливаете заголовок как встроенный для представления, в котором вы хотите удалить пространство, этого не нужно делать для представления с NavigationView, но оно также перемещается.

.navigationBarTitle("", displayMode: .inline)

затем просто измените внешний вид панелей навигации

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

Поместите в свой NextView следующий код

              .navigationBarBackButtonHidden(true)
        .navigationBarHidden(true)

Но при переходе к NextView через NavigationLink вы также должны ввести такой модификатор:

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

Я также попробовал все решения, упомянутые на этой странице, и нашел только решение @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())
    }
}

Это ошибка, присутствующая в 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())
    }
}

⚠️Заголовок указывать не нужно.

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

Вы можете расширить собственный протокол просмотра следующим образом:

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

Тогда просто позвоните, например:

ZStack {
    *YOUR CONTENT*
}
.hideNavigationBar()

Для меня это произошло потому, что я выталкивал свой NavigationView из существующего. Фактически, одно внутри другого. Если вы переходите из NavigationView, вам не нужно создавать его внутри следующего, поскольку вы уже находитесь внутри NavigatonView.

Я пробовал настроить .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

Мое решение этой проблемы было таким же, как предложено @Genki и @Frankenstein.

Я применил два модификатора к внутреннему списку (НЕ NavigationView), чтобы избавиться от пробелов:

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

На внешнем NavigationView, затем применили .navigationBarTitle("TITLE") установить заголовок.

Та же проблема, я наконец решил. Чтобы навигация полностью исчезла, вам нужно добавить эти модификаторы в NavigationViewИ ВСЕ NavigationsLinksвнутри него:

      .navigationBarHidden(true)
.navigationBarTitleDisplayMode(.inline)

Если вы не сделаете это также с NavigationLinksне будет работать.

Я пытаюсь добавить .navigationBarHidden(true) в конец фигурных скобок моего Vstack, как это

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

и панель навигации исчезнет, но если я добавлю .navigationBarHidden(true) в конец фигурных скобок панели навигации, как это

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

панель навигации не исчезает

Аналогично ответу @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
                }
        }
    }
}

Установка заголовка необходима, поскольку он отображается рядом с кнопкой "Назад" в представлениях, к которым вы переходите.

У меня была аналогичная проблема при работе с приложением, в котором TabView должен отображаться после входа пользователя в систему.

Как предложил @graycampbell в своем комментарии, TabView не следует встраивать в NavigationView, иначе появится "пустое пространство", даже при использовании .navigationBarHidden(true)

Я использовал ZStackчтобы скрыть NavigationView. Обратите внимание, что в этом простом примере я использую@State а также @Binding для управления видимостью пользовательского интерфейса, но вы можете использовать что-то более сложное, например объект среды.

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

Некоторое время я боролся с этим, но в итоге у меня сработало ...

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

Мне нужно перейти с экрана 1 на экран 2. Если я использую это для NavigationView, как указано выше, панель навигации будет скрыта, но ее пространство все еще существует (количество места с высотой) на экране 1.

Наконец, у меня есть собственное решение, которое использует этот код в любом представлении внутри NavigationView и не заботится о navigationBarTitle. Именно так:

Экран1:

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

Экран2:

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

У меня была такая же проблема, и я обнаружил, что следующий код работает лучше всего.

         .navigationTitle("")
   .navigationBarBackButtonHidden(true)

Это, безусловно, самый простой и стабильный подход, который я нашел. Вы можете скрыть как заголовок навигации, так и кнопку «Назад», скрыв всю панель инструментов. Вы также можете показать его в любом представлении, которое вы хотите. Вы можете скрыть его, используя.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)

Попробуйте поместить NavigationView в GeometryReader.

GeometryReader {
    NavigationView {

        Text( “Hello World!” )

    }
}

Я испытал странное поведение, когда NavigationView был корневым представлением.

Я знаю, что немного опоздал, но я только что исправил эту проблему, используя главный ответ здесь:Как избавиться от пробела во вложенном NavigationView с помощью SwiftUI

Если содержимое этой страницы изменится, я объясню ответ ниже.

Используйте оболочку NavigationView только на самом верхнем уровне любого представления, которому требуется навигация, независимо от того, как далеко спускаются вложенные дочерние элементы. Все они уже будут иметь свойства NavigationView, и вы можете вызывать NavigationLink в любое время внутри подвидов. У меня было много дополнительных оболочек NavigationView вокруг вложенных представлений, их удаление удалило лишнее пустое пространство, сохранив при этом функциональность всех навигационных ссылок.

Мне очень понравилась идея, данная @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())
    }
}
Другие вопросы по тегам