Am Ende habe ich die Standardeinstellung überschrieben NavigationView
und NavigationLink
das gewünschte Verhalten erhalten. Dies scheint so einfach zu sein, dass ich etwas übersehen muss, was die Standard-SwiftUI-Ansichten tun?
NavigationView
Ich wickle ein UINavigationController
in ein super einfaches UIViewControllerRepresentable
, das UINavigationController
die SwiftUI-Inhaltsansicht als Umgebungsobjekt gibt. Dies bedeutet, dass der NavigationLink
später darauf zugreifen kann, solange er sich im selben Navigationscontroller befindet (die dargestellten Ansichtscontroller erhalten die Umgebungsobjekte nicht), was genau das ist, was wir wollen.
Hinweis: Die NavigationView benötigt .edgesIgnoringSafeArea(.top)
und ich weiß noch nicht, wie ich das in der Struktur selbst einstellen soll. Siehe Beispiel, wenn Ihr NVC oben abschneidet.
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
Ich erstelle einen benutzerdefinierten NavigationLink, der auf die Umgebungen UINavigationController zugreift, um einen UIHostingController zu pushen, der die nächste Ansicht hostet.
Hinweis: Ich habe das selection
und isActive
das SwiftUI.NavigationLink nicht implementiert , da ich noch nicht vollständig verstehe, was sie tun. Wenn Sie dabei helfen möchten, kommentieren / bearbeiten Sie diese bitte.
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)
}
}
Dies behebt, dass das Zurückwischen unter SwiftUI nicht richtig funktioniert. Da ich die Namen NavigationView und NavigationLink verwende, wurde mein gesamtes Projekt sofort auf diese umgestellt.
Beispiel
Im Beispiel zeige ich auch die modale Darstellung.
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")
}
}
}
Bearbeiten: Ich begann mit "Das scheint so einfach, dass ich etwas übersehen muss" und ich denke, ich habe es gefunden. Dies scheint EnvironmentObjects nicht in die nächste Ansicht zu übertragen. Ich weiß nicht, wie der Standard-Navigationslink dies tut. Daher sende ich Objekte vorerst manuell an die nächste Ansicht, in der ich sie benötige.
NavigationLink(destination: Text("Detail").environmentObject(objectToSendOnToTheNextView)) {
Text("Show detail")
}
Bearbeiten 2:
Dadurch wird der Navigationscontroller allen Ansichten im Inneren NavigationView
ausgesetzt @EnvironmentObject var nvc: UINavigationController
. Um dies zu beheben, wird das EnvironmentObject, mit dem wir die Navigation verwalten, zu einer dateiprivaten Klasse. Ich habe dies im Kern behoben: https://gist.github.com/Amzd/67bfd4b8e41ec3f179486e13e9892eeb