J'ai fini par remplacer la valeur par défaut NavigationView
et NavigationLink
obtenir le comportement souhaité. Cela semble si simple que je dois ignorer quelque chose que les vues SwiftUI par défaut font?
NavigationView
J'enveloppe un UINavigationController
dans un super simple UIViewControllerRepresentable
qui donne UINavigationController
à la vue du contenu SwiftUI comme un objet environnement. Cela signifie que vous NavigationLink
pouvez le récupérer plus tard tant qu'il se trouve dans le même contrôleur de navigation (les contrôleurs de vue présentés ne reçoivent pas les objets d'environnement), ce qui est exactement ce que nous voulons.
Remarque: La NavigationView a besoin .edgesIgnoringSafeArea(.top)
et je ne sais pas encore comment définir cela dans la structure elle-même. Voir l'exemple si votre nvc coupe en haut.
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
Je crée un NavigationLink personnalisé qui accède aux environnements UINavigationController pour pousser un UIHostingController hébergeant la vue suivante.
Remarque: Je n'ai pas implémenté le selection
et isActive
que SwiftUI.NavigationLink a parce que je ne comprends pas encore complètement ce qu'ils font. Si vous souhaitez nous aider, veuillez commenter / modifier.
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)
}
}
Cela résout le balayage arrière ne fonctionnant pas correctement sur SwiftUI et parce que j'utilise les noms NavigationView et NavigationLink, mon projet entier est passé immédiatement à ceux-ci.
Exemple
Dans l'exemple, je montre également la présentation modale.
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")
}
}
}
Edit: J'ai commencé avec "Cela semble si simple que je dois oublier quelque chose" et je pense que je l'ai trouvé. Cela ne semble pas transférer EnvironmentObjects à la vue suivante. Je ne sais pas comment le NavigationLink par défaut fait cela, donc pour l'instant j'envoie manuellement des objets à la vue suivante où j'en ai besoin.
NavigationLink(destination: Text("Detail").environmentObject(objectToSendOnToTheNextView)) {
Text("Show detail")
}
Modifier 2:
Cela expose le contrôleur de navigation à toutes les vues à l'intérieur NavigationView
en faisant @EnvironmentObject var nvc: UINavigationController
. La façon de résoudre ce problème fait de l'environnementObjet que nous utilisons pour gérer la navigation une classe fileprivate. J'ai corrigé cela dans l'essentiel: https://gist.github.com/Amzd/67bfd4b8e41ec3f179486e13e9892eeb