Skończyłem na tym, że nadpisałem domyślne ustawienie NavigationView
i uzyskałem NavigationLink
pożądane zachowanie. To wydaje się tak proste, że muszę przeoczyć coś, co robią domyślne widoki SwiftUI?
NavigationView
I zawinąć UINavigationController
w super proste UIViewControllerRepresentable
, że daje UINavigationController
do Treści SwiftUI jako environmentObject. Oznacza to, że NavigationLink
można później pobrać, o ile znajduje się w tym samym kontrolerze nawigacyjnym (kontrolery widoku nie otrzymują obiektów środowiskowych), co jest dokładnie tym, czego chcemy.
Uwaga: NavigationView potrzebuje .edgesIgnoringSafeArea(.top)
i nie wiem jeszcze, jak ustawić to w samej strukturze. Zobacz przykład, jeśli Twój nvc odcina się u góry.
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
Tworzę niestandardowy NavigationLink, który uzyskuje dostęp do środowiska UINavigationController, aby przekazać interfejs UIHostingController obsługujący następny widok.
Uwaga: Nie wdrożenia selection
i isActive
że SwiftUI.NavigationLink ma, bo nie w pełni zrozumieć, co jeszcze robią. Jeśli chcesz pomóc, napisz komentarz / edytuj.
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)
}
}
To rozwiązuje problem, że machnięcie wstecz nie działa poprawnie w SwiftUI, a ponieważ używam nazw Nawigacja i Nawigacja, mój cały projekt został natychmiast przełączony na te.
Przykład
W tym przykładzie pokazuję również prezentację modalną.
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")
}
}
}
Edycja: Zacząłem od „To wydaje się tak proste, że muszę coś przeoczyć” i myślę, że to znalazłem. Wydaje się, że to nie przenosi EnvironmentObjects do następnego widoku. Nie wiem, jak robi to domyślny NavigationLink, więc na razie ręcznie wysyłam obiekty do następnego widoku, w którym ich potrzebuję.
NavigationLink(destination: Text("Detail").environmentObject(objectToSendOnToTheNextView)) {
Text("Show detail")
}
Edycja 2:
Naraża to kontroler nawigacyjny do wszystkich poglądów wewnątrz NavigationView
robiąc @EnvironmentObject var nvc: UINavigationController
. Sposobem na rozwiązanie tego problemu jest uczynienie środowiska obiektem, którego używamy do zarządzania nawigacją, klasą plików nieprywatną. Naprawiłem to w gist: https://gist.github.com/Amzd/67bfd4b8e41ec3f179486e13e9892eeb