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

94

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

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

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

Я хочу добиться этого

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

Я пробовал использовать

.navigationBarHidden(true)

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

В настоящее время я настраиваю свой NavigiationView следующим образом

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

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

Vapidant
источник

Ответы:

146

По какой - то причине, 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")
    }
}

Установка navigationBarHiddenв связи falseс соображениями глубже в навигации стеке , кажется, не правильно переопределить предпочтение мнения, изначально установленные navigationBarHiddenна true, так что единственный обходный маневр я мог придумать использую привязку изменить предпочтение первоначального вида , когда новый представление помещается в стек навигации.

Как я уже сказал, это хакерское решение, но без официального решения от Apple это лучшее, что я смог придумать.

Graycampbell
источник
5
Это устранило мою проблему! Это очень странно, что у вас должен быть заголовок, прежде чем вы сможете скрыть панель навигации ...
Vapidant
5
Ошибка все еще существует вне бета-версии: /
Дэниел Райан
1
@Peacemoon Раньше я этого не замечал. В общем, кажется, что реализация от Apple здесь довольно неаккуратная. Вы не должны установить заголовок просто скрыть строку , чтобы начать с, и установив navigationBarHiddenв falseна следующее представление должно UNHIDE панель навигации, но это не так. В конце концов мне надоело, насколько плохо документирован SwiftUI, и я вернулся к UIKit, а тот факт, что по крайней мере 20 человек пришли сюда, чтобы узнать, как скрыть панель навигации, довольно плохо говорит о реализации и / или документации Apple. Извините, у меня нет лучшего ответа для вас.
graycampbell
2
@SambitPrakash Я никогда раньше не вкладывал TabView в NavigationView, и, насколько я могу судить, Apple, похоже, не вкладывает их в свои приложения таким образом. Мне никогда не было полностью ясно, предполагается ли вообще вложение TabView в NavigationView, и я знаю, что в SwiftUI были некоторые странные ошибки, которые всплывают, когда вы вкладываете их таким образом. TabViews всегда казались мне формой навигации более высокого уровня, чем NavigationViews. Если вместо этого вы вложите NavigationView в TabView, я считаю, что мой обходной путь все равно должен работать.
graycampbell
2
@kar Жаль, что этот ответ все еще привлекает внимание и поддерживает. Я написал это как временное решение проблемы, которая должна была быть временной. В последнее время не тестировал, но проблем с ним явно много. Несколько человек также спросили, можете ли вы перемещаться между представлениями без использования NavigationView. Ответ - да, но вам, по сути, придется написать свой собственный NavigationView с нуля. Вы не можете просто волшебным образом перемещаться между представлениями. Что-то должно управлять этими представлениями и обеспечивать переходы между ними, поэтому у нас есть NavigationView.
graycampbell
18

Цель a 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.

Течение_
источник
29
Есть причины, по которым вы можете захотеть скрыть панель навигации, сохранив при этом функциональность NavigationView. Цель a NavigationView- не только для отображения панели навигации.
graycampbell
6
Мне нужен NavigiationView для функциональности навигации в стеке и возможности легко возвращаться из представлений, мне не нужна панель навигации в начальном представлении.
Vapidant
2
Есть ли способ перемещаться по представлениям без навигацииView?
user1445685
В принципе. Нет. По крайней мере, пока не на swiftui
Густаво Паррадо
Этот ответ бесполезен, поскольку наличие NavigationView влияет на исходный вопрос, поскольку он необходим для перехода к другому представлению.
JaseTheAce
16

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

//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
10

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

.navigationBarTitle("", displayMode: .inline)

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

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

в представлении, содержащем исходный NavigationView. окончательное решение

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

MindBlower3
источник
1
Это полезное решение
Мурило Медейрос
9

Я также пробовал все решения, упомянутые на этой странице, и нашел только решение @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())
    }
}
Фемкео
источник
2
использование @EnvironmentObject намного лучше для передачи данных по всему приложению, чем @State , поэтому я предпочитаю, чтобы вы ответили больше
Арафин Рассел
7

Это ошибка, присутствующая в 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
При этом жест «быстро к спине» больше не работает
Уркман
6

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

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

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

ZStack {
    *YOUR CONTENT*
}
.hideNavigationBar()
Hopreeeenjust
источник
5

Для меня я применял .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)
        }
    }
}

Панель навигации и список без пробелов вверху

Генки
источник
5
Не имеет отношения к заданному вопросу
Ахмед Сахиб
3
@AhmedSahib Вопрос был в том, «Как удалить пространство панели навигации по умолчанию в SwiftUI NavigiationView», и мой код справляется с этим.
Genki
1
Отличный совет. Для моего решения мне пришлось применить два модификатора к внутреннему списку, чтобы избавиться от пробелов: .navigationBarTitle ("", displayMode: .automatic) .navigationBarHidden (true) Затем во внешнем NavigationView мне пришлось применить: .navigationBarTitle (" TITLE ", displayMode: .inline)
Франкенштейн
4

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

RyanTCB
источник
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
                }
        }
    }
}

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

Фабиан Штрайтель
источник
1

SwiftUI 2

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

.navigationBarTitleDisplayMode(.inline)

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

pawello2222
источник
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для управления видимостью пользовательского интерфейса, но вы можете использовать что-то более сложное, например объект среды.

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
-8

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

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

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

Стивен Маркотт
источник