Terminé anulando el valor predeterminado NavigationView
y NavigationLink
para obtener el comportamiento deseado. Esto parece tan simple que debo pasar por alto algo que hacen las vistas predeterminadas de SwiftUI.
NavigationView
Envuelvo un UINavigationController
en un super simple UIViewControllerRepresentable
que le da UINavigationController
a la vista de contenido de SwiftUI como un objeto de entorno. Esto significa NavigationLink
que más tarde puede captar eso, siempre y cuando esté en el mismo controlador de navegación (los controladores de vista presentados no reciben el entorno Objetos), que es exactamente lo que queremos.
Nota: El NavigationView necesita .edgesIgnoringSafeArea(.top)
y aún no sé cómo configurarlo en la estructura misma. Vea el ejemplo si su nvc se corta en la parte superior.
struct NavigationView<Content: View>: UIViewControllerRepresentable {
var content: () -> Content
init(@ViewBuilder content: @escaping () -> Content) {
self.content = content
}
func makeUIViewController(context: Context) -> UINavigationController {
let nvc = UINavigationController()
let host = UIHostingController(rootView: content().environmentObject(nvc))
nvc.viewControllers = [host]
return nvc
}
func updateUIViewController(_ uiViewController: UINavigationController, context: Context) {}
}
extension UINavigationController: ObservableObject {}
NavigationLink
Creo un NavigationLink personalizado que accede a los entornos UINavigationController para impulsar un UIHostingController que aloja la siguiente vista.
Nota: No implementé el selection
y isActive
el SwiftUI.NavigationLink lo tiene porque todavía no entiendo completamente lo que hacen. Si quieres ayudar con eso, comenta / edita.
struct NavigationLink<Destination: View, Label:View>: View {
var destination: Destination
var label: () -> Label
public init(destination: Destination, @ViewBuilder label: @escaping () -> Label) {
self.destination = destination
self.label = label
}
/// If this crashes, make sure you wrapped the NavigationLink in a NavigationView
@EnvironmentObject var nvc: UINavigationController
var body: some View {
Button(action: {
let rootView = self.destination.environmentObject(self.nvc)
let hosted = UIHostingController(rootView: rootView)
self.nvc.pushViewController(hosted, animated: true)
}, label: label)
}
}
Esto resuelve que el deslizamiento hacia atrás no funcione correctamente en SwiftUI y debido a que uso los nombres NavigationView y NavigationLink, todo mi proyecto cambió a estos de inmediato.
Ejemplo
En el ejemplo también muestro presentación modal.
struct ContentView: View {
@State var isPresented = false
var body: some View {
NavigationView {
VStack(alignment: .center, spacing: 30) {
NavigationLink(destination: Text("Detail"), label: {
Text("Show detail")
})
Button(action: {
self.isPresented.toggle()
}, label: {
Text("Show modal")
})
}
.navigationBarTitle("SwiftUI")
}
.edgesIgnoringSafeArea(.top)
.sheet(isPresented: $isPresented) {
Modal()
}
}
}
struct Modal: View {
@Environment(\.presentationMode) var presentationMode
var body: some View {
NavigationView {
VStack(alignment: .center, spacing: 30) {
NavigationLink(destination: Text("Detail"), label: {
Text("Show detail")
})
Button(action: {
self.presentationMode.wrappedValue.dismiss()
}, label: {
Text("Dismiss modal")
})
}
.navigationBarTitle("Modal")
}
}
}
Editar: Comencé con "Esto parece tan simple que debo pasar por alto algo" y creo que lo encontré. Esto no parece transferir EnvironmentObjects a la siguiente vista. No sé cómo lo hace el NavigationLink predeterminado, así que por ahora envío manualmente objetos a la siguiente vista donde los necesito.
NavigationLink(destination: Text("Detail").environmentObject(objectToSendOnToTheNextView)) {
Text("Show detail")
}
Edición 2:
Esto expone el controlador de navegación a todas las vistas dentro NavigationView
haciendo @EnvironmentObject var nvc: UINavigationController
. La forma de solucionar esto es hacer del entorno object que utilizamos para administrar la navegación una clase privada de archivos. Arreglé esto en la esencia: https://gist.github.com/Amzd/67bfd4b8e41ec3f179486e13e9892eeb
extension UINavigationController: ObservableObject {}
Puede hacerlo descendiendo a UIKit y utilizando su propio UINavigationController.
Primero crea un
SwipeNavigationController
archivo:Esto es lo mismo
SwipeNavigationController
provisto aquí , con la adición de lapushSwipeBackView()
función.Esta función requiere una
SwipeBackHostingController
que definimos comoLuego configuramos las aplicaciones
SceneDelegate
para usarSwipeNavigationController
:Finalmente úsalo en tu
ContentView
:fuente
func navController()
Tomar el vc y luego empujar el vc usted mismo es realmente una gran idea y me ayudó a resolver este problema! Contestaré una respuesta más amigable de SwiftUI, ¡pero gracias por su ayuda!