Як видалити простір панелі навігації за замовчуванням у SwiftUI NavigiationView


95

Я новачок у 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: "")
}
}

Я хочу зазначити, що кінцевою метою тут є те, що ви зможете натиснути на ці клітинки, щоб глибше перейти до дерева файлів, і, отже, на панелі глибшої навігації повинна відображатися кнопка "Назад", але я не хочу нічого в зверху як такий під час мого первісного бачення.

Відповіді:


147

З якоїсь причини 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 це найкраще, що мені вдалося придумати.


5
Це вирішило мою проблему! Це дуже дивно, що перед тим, як приховати навігаційну панель, вам потрібно мати заголовок ...
Vapidant

5
Помилка все ще знаходиться поза бета-версією: /
Даніель Райан,

1
@Peacemoon Я раніше цього не помічав. Загалом, здається, що реалізація від Apple тут досить неакуратна. Ви не повинні встановити заголовок просто приховати рядок , щоб почати с, і встановивши navigationBarHiddenв falseна наступне подання повинно UNHIDE панель навігації, але це не так. Зрештою, я набрид, наскільки погано задокументований SwiftUI, і повернувся до UIKit, і той факт, що щонайменше 20 людей прийшли сюди лише для того, щоб навчитися приховувати навігаційну панель, досить погано говорить про реалізацію та / або документацію Apple. Вибачте, у мене немає кращої відповіді для вас.
greycampbell

2
@SambitPrakash Я ніколи раніше не вклав TabView всередину NavigationView, і, схоже, Apple не вкладає їх таким чином у свої програми, наскільки я можу зрозуміти. Мені ніколи не було цілком зрозуміло, чи вкладання TabView всередину NavigationView взагалі призначене, і я знаю, що SwiftUI мав якісь дивні помилки, які з’являються, коли ви вкладаєте їх таким чином. TabViews завжди відчував себе формою навігації вищого рівня, ніж NavigationViews для мене. Якщо ви замість цього вкладете NavigationView всередину TabView, я вважаю, що мій спосіб вирішення проблеми все одно повинен працювати.
greycampbell

2
@kar Розчаровує те, що ця відповідь все ще привертає увагу та прихильність. Я написав це як тимчасове рішення того, що мало б бути тимчасовою помилкою. Я не тестував його нещодавно, але, очевидно, з цим є багато проблем. Кілька людей також запитали, чи можна переміщатися між поданнями, не використовуючи NavigationView. Відповідь - так, але вам, по суті, доведеться писати власний NavigationView з нуля. Ви не можете просто магічно орієнтуватися між поданнями. Щось має управляти цими поданнями та забезпечувати переходи між ними, саме тому ми маємо NavigationView.
greycampbell

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
Є причини, чому ви можете заховати навігаційну панель, зберігаючи при цьому функціональність a NavigationView. Призначення a NavigationViewне лише для відображення панелі навігації.
greycampbell

6
Я хочу NavigiationView для функціональності навігації в стеку та можливості повернення з подань легко, мені не потрібна панель navigiationBar у початковому поданні.
Вапідант

2
Чи існує спосіб навігації по представленнях без NaviView?
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. остаточне рішення

Якщо ви хочете змінити зовнішній вигляд з екрана на екран, змініть зовнішній вигляд у відповідних поданнях


1
Це рішення є корисним
Murilo Medeiros

9

Я також спробував усі рішення, згадані на цій сторінці, і знайшов лише рішення @graycampbell, яке добре працює, з добре працюючою анімацією. Тому я спробував створити значення, яке я можу просто використовувати у всій програмі, до якого я можу отримати доступ де завгодно, на прикладі hackingwithswift.com

Я створив ObservableObjectклас

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

І передайте це на початковий вигляд SceneDelegateприблизно так

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

Тоді в ContentViewми можемо відстежувати цей спостережуваний об'єкт так і створити посилання на 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). Я написав a, 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

Ви можете розширити вбудований протокол View таким чином:

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

Тоді просто зателефонуйте наприклад:

ZStack {
    *YOUR CONTENT*
}
.hideNavigationBar()

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", і мій код це робить.
Генкі

1
Чудова порада. Для свого рішення мені довелося застосувати два модифікатори до внутрішнього списку, щоб позбутися від пробілів: .navigationBarTitle ("", displayMode: .automatic) .navigationBarHidden (true) Потім у зовнішньому NavigationView мені довелося застосувати: .navigationBarTitle (" TITLE ", displayMode: .inline)
Франкенштейн

4

Для мене це було тому, що я виштовхував свій NavigationView з існуючого. Фактично маючи одне всередині іншого. Якщо ви виходите з NavigationView, вам не потрібно створювати його всередині наступного, як це вже було в NavigatonView.


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

SwiftUI 2

Існує спеціальний модифікатор, щоб панель навігації займала менше місця:

.navigationBarTitleDisplayMode(.inline)

Вам більше не потрібно приховувати панель навігації або встановлювати її назву.


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)

Я використав a, 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всередину a GeometryReader.

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

Я відчував дивну поведінку, коли це NavigationViewбув кореневий погляд.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.