Cómo eliminar el espacio predeterminado de la barra de navegación en SwiftUI NavigiationView

83

Soy nuevo en SwiftUI (como la mayoría de las personas) y trato de descubrir cómo eliminar algunos espacios en blanco sobre una lista que incrusté en un NavigationView

En esta imagen, puede ver que hay un espacio en blanco sobre la Lista

Versión actual

Lo que quiero lograr es esto

Versión ideal

He intentado usar

.navigationBarHidden(true)

pero esto no hizo cambios notables.

Actualmente estoy configurando mi navigiationView así

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

donde FileBrowserView es una vista con una lista y celdas definidas así

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

Quiero señalar que el objetivo final aquí es que podrá hacer clic en estas celdas para navegar más profundamente en un árbol de archivos y, por lo tanto, debería mostrar un botón Atrás en la barra de navegación más profunda, pero no quiero nada en el top como tal durante mi vista inicial.

Vaporizante
fuente

Respuestas:

132

Por alguna razón, SwiftUI requiere que también se configure .navigationBarTitlepara .navigationBarHiddenque funcione correctamente.

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

Actualizar

Como @Peacemoon señaló en los comentarios, los restos de barra de exploración ocultos a medida que navega más profundo en la pila de navegación, independientemente de si o no se establece navigationBarHiddena falselas vistas posteriores. Como dije en los comentarios, esto es el resultado de una implementación deficiente por parte de Apple o simplemente una documentación terrible (quién sabe, tal vez haya una forma "correcta" de lograr esto).

Cualquiera que sea el caso, se me ocurrió una solución que parece producir los resultados deseados del póster original. Dudo en recomendarlo porque parece innecesariamente hack, pero sin una forma sencilla de ocultar y mostrar la barra de navegación, esto es lo mejor que puedo hacer.

Este ejemplo utiliza tres vistas: View1tiene una barra de navegación oculta View2y View3ambas tienen barras de navegación visibles con títulos.

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

Ajuste navigationBarHiddena falseen las vistas más profundo en la pila de navegación no parece anular correctamente la preferencia de la opinión de que en un principio establecido navigationBarHiddenque true, por lo que la única solución que se me ocurrió con utilizaba una unión a cambiar la preferencia de la vista original cuando una nueva La vista se inserta en la pila de navegación.

Como dije, esta es una solución hacky, pero sin una solución oficial de Apple, esta es la mejor que he podido encontrar.

campanilla gris
fuente
5
¡Esto solucionó mi problema! Es muy extraño que tenga que tener un título antes de poder ocultar la barra de navegación ...
Vapidant
5
El error sigue ahí fuera de la versión beta: /
Daniel Ryan
1
@Peacemoon No me di cuenta de eso antes. Con todo, parece que la implementación de Apple es bastante descuidada aquí. Usted no debería tener que establecer el título sólo para ocultar la barra para empezar, y el establecimiento navigationBarHiddende falsela siguiente vista debe mostrar la barra de navegación, pero no es así. Finalmente, me cansé de lo mal documentado que estaba SwiftUI y volví a UIKit, y el hecho de que al menos 20 personas vinieran aquí solo para aprender a ocultar la barra de navegación habla bastante mal de la implementación y / o documentación de Apple. Lo siento, no tengo una mejor respuesta para ti.
graycampbell
2
@SambitPrakash Nunca antes había anidado un TabView dentro de un NavigationView, y Apple no parece anidarlos de esa manera en sus aplicaciones hasta donde yo sé. Nunca me ha quedado completamente claro si se pretende anidar un TabView dentro de un NavigationView, y sé que SwiftUI ha tenido algunos errores extraños que aparecen cuando los anida de esa manera. TabViews siempre me ha parecido una forma de navegación de mayor nivel que NavigationViews para mí. Si, en cambio, anida NavigationView dentro de TabView, creo que mi solución debería funcionar.
graycampbell
2
@kar Es decepcionante que esta respuesta todavía reciba atención y votos positivos. Lo escribí como una solución temporal a lo que debería haber sido un error temporal. No lo he probado recientemente, pero obviamente tiene muchos problemas. Varias personas también han preguntado si puede navegar entre vistas sin usar NavigationView. La respuesta es sí, pero esencialmente tendría que escribir su propio NavigationView desde cero. No puede simplemente navegar mágicamente entre vistas. Algo tiene que administrar esas vistas y proporcionar transiciones entre ellas, por eso tenemos NavigationView.
graycampbell
17

El propósito de a NavigationViewes agregar la barra de navegación en la parte superior de su vista. En iOS, hay 2 tipos de barras de navegación: grandes y estándar.

ingrese la descripción de la imagen aquí

Si no desea una barra de navegación:

FileBrowserView(jsonFromCall: URLRetrieve(URLtoFetch: applicationDelegate.apiURL))

Si desea una barra de navegación grande (generalmente se usa para sus vistas de nivel superior):

NavigationView {
    FileBrowserView(jsonFromCall: URLRetrieve(URLtoFetch: applicationDelegate.apiURL))
    .navigationBarTitle(Text("Title"))
}

Si desea una barra de navegación estándar (en línea) (generalmente se usa para vistas de subnivel):

NavigationView {
    FileBrowserView(jsonFromCall: URLRetrieve(URLtoFetch: applicationDelegate.apiURL))
    .navigationBarTitle(Text("Title"), displayMode: .inline)
}

Espero que esta respuesta te ayude.

Más información: Documentación de Apple

El flujo_
fuente
26
Hay razones por las que es posible que desee ocultar la barra de navegación y al mismo tiempo mantener la funcionalidad de un NavigationView. El propósito de a NavigationViewno es únicamente mostrar una barra de navegación.
graycampbell
6
Quiero NavigiationView para la funcionalidad de navegar en la pila y tener la capacidad de volver fácilmente desde las vistas, no necesito una barra de navegación en la vista inicial.
Vapidant
2
¿Hay alguna forma de navegar por las vistas sin navigationView?
user1445685
Básicamente. No. Todavía no en swiftui al menos
Gustavo Parrado
Esta respuesta no es útil, ya que tener un NavigationView tiene un impacto en la pregunta original, ya que es necesario para navegar a otra vista.
JaseTheAce
14

Ver modificadores lo hizo fácil:

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

Ejemplo: ingrese la descripción de la imagen aquí

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()
        }
    }
}
Peter Kreinz
fuente
4
No soluciona el problema de un controlador de vista empujado.
Damjandd
Parece clave aquí que el modificador no se agrega a NavigationView sino a la vista que está dentro. Esto marcó la diferencia para que funcionara. ¡Gracias! :-)
JaseTheAce
9

También probé todas las soluciones mencionadas en esta página y solo encontré la solución @graycampbell que funciona bien, con animaciones que funcionan bien. Así que traté de crear un valor que pueda usar en toda la aplicación al que pueda acceder desde cualquier lugar con el ejemplo de hackingwithswift.com

Creé una ObservableObjectclase

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

Y pasarlo a la vista inicial de la SceneDelegatemisma manera

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

Luego, en el ContentViewpodemos realizar un seguimiento de este objeto observable y crear un enlace a 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
            }
        }
    }
}

Y luego al acceder a la segunda vista (SomeView), la volvemos a ocultar así:

struct SomeView: View {
    @EnvironmentObject var navBarPrefs: NavBarPreferences

    var body: some View {
        Text("Hello second screen")
        .onAppear {
            self.navBarPrefs.navBarIsHidden = false
        }
    } 
}

Para que las vistas previas sigan funcionando, agregue NavBarPreferences a la vista previa de la siguiente manera:

struct SomeView_Previews: PreviewProvider {
    static var previews: some View {
        SomeView().environmentObject(NavBarPreferences())
    }
}
Femkeo
fuente
2
usar @EnvironmentObject es mucho mejor para pasar datos a través de la aplicación en lugar de @State , así que prefiero que responda más
Arafin Russell
6

Este es un error presente en SwiftUI ( aún a partir de Xcode 11.2.1). Escribí un ViewModifierpara solucionar esto, basado en el código de las respuestas existentes:

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())
    }
}
Vatsal Manot
fuente
2
Con esto, el gesto de "rápido hacia atrás" ya no funciona
Urkman
6

Si configura el título como en línea para la Vista en la que desea eliminar el espacio, no es necesario hacerlo en una vista con NavigationView, sino también en la vista navegada.

.navigationBarTitle("", displayMode: .inline)

Problema inicial solución 1 luego simplemente cambie la apariencia de las barras de navegación

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

en la vista que contiene el NavigationView inicial. solución final

Si desea cambiar la apariencia de una pantalla a otra, cambie la apariencia en las vistas apropiadas

MindBlower3
fuente
Esta solución es útil
Murilo Medeiros
5

Puede extender el protocolo de vista nativo de esta manera:

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

Entonces solo llame por ejemplo:

ZStack {
    *YOUR CONTENT*
}
.hideNavigationBar()
Hopreeeenjust
fuente
4

Para mí, estaba aplicando el .navigationBarTitleal NavigationViewy no Listel culpable. Esto me funciona en 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)
        }
    }
}

Barra de navegación y lista sin espacios en la parte superior

Genki
fuente
5
Irrelevante para la pregunta formulada
Ahmed Sahib
3
@AhmedSahib La pregunta era "Cómo eliminar el espacio predeterminado de la barra de navegación en SwiftUI NavigiationView" y mi código lo logra.
Genki
1
Excelente consejo. Para mi solución, tuve que aplicar dos modificadores a la lista interna para deshacerme del espaciado: .navigationBarTitle ("", displayMode: .automatic) .navigationBarHidden (true) Luego, en el NavigationView externo tuve que aplicar: .navigationBarTitle (" TITLE ", displayMode: .inline)
Frankenstein
2

Similar a la respuesta de @graycampbell pero un poco más simple:

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

Es necesario establecer el título, ya que se muestra junto al botón Atrás en las vistas a las que navega.

Fabián Streitel
fuente
2

Para mí fue porque estaba empujando mi NavigationView desde un archivo. En efecto, tener uno dentro del otro. Si viene de un NavigationView, no necesita crear uno dentro del siguiente, ya que ya está dentro de un NavigatonView.

RyanTCB
fuente
0

Realmente me encantó la idea de @Vatsal Manot de crear un modificador para esto.
Eliminar la isHiddenpropiedad de su respuesta, ya que no lo encuentro útil, ya que el nombre del modificador sugiere que oculta la barra de navegación.

// 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())
    }
}
Kiran Jasvanee
fuente
0

Tuve un problema similar al trabajar en una aplicación donde debería mostrarse un TabView una vez que el usuario inició sesión.

Como sugirió @graycampbell en su comentario, un TabView no debe estar incrustado en un NavigationView, o de lo contrario aparecerá el "espacio en blanco", incluso cuando se usa .navigationBarHidden(true)

Usé un ZStackpara ocultar NavigationView. Tenga en cuenta que para este ejemplo simple, utilizo @Statey @Bindingpara administrar la visibilidad de la interfaz de usuario, pero es posible que desee usar algo más complejo, como un objeto de entorno.

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

Cuando presionamos el botón Iniciar sesión, la página inicial desaparece y se carga DetailView. La página de inicio de sesión vuelve a aparecer cuando cambiamos el botón Cerrar sesión

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")
            }
        }
    }
}
Sala
fuente
0

Mi solución para este problema fue la misma que sugirieron @Genki y @Frankenstein.

Apliqué dos modificadores a la lista interna (NO al NavigationView) para deshacerme del espaciado:

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

En el NavigationView exterior, luego se aplica .navigationBarTitle("TITLE")para establecer el título.

LochNessMonster
fuente
1
No hace nada.
TruMan1
0

SwiftUI 2

Hay un modificador dedicado para hacer que la barra de navegación ocupe menos espacio:

.navigationBarTitleDisplayMode(.inline)

Ya no es necesario ocultar la barra de navegación ni establecer su título.

pawello2222
fuente
-7

Intente poner el NavigationViewinterior a GeometryReader.

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

Experimenté un comportamiento extraño cuando NavigationViewera la vista raíz.

Steven Marcotte
fuente