В итоге я переопределил настройки по умолчанию NavigationView
и NavigationLink
получил желаемое поведение. Это кажется настолько простым, что я должен пропустить что-то, что делают стандартные представления SwiftUI?
NavigationView
Я обернул UINavigationController
в супер простой, UIViewControllerRepresentable
который дает UINavigationController
представление содержимого SwiftUI как environmentObject. Это означает, что NavigationLink
позже можно получить, что он находится в том же контроллере навигации (представленные контроллеры представления не получают environmentObjects), что именно то, что нам нужно.
Примечание: NavigationView нуждается, .edgesIgnoringSafeArea(.top)
и я пока не знаю, как установить это в самой структуре. Смотрите пример, если ваш nvc обрезается сверху.
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
Я создаю пользовательский NavigationLink, который обращается к средам UINavigationController, чтобы протолкнуть UIHostingController, на котором размещено следующее представление.
Примечание: я не реализовал selection
и isActive
что SwiftUI.NavigationLink имеет, потому что я не до конца понимаю, что они делают. Если вы хотите помочь с этим, пожалуйста, комментируйте / редактируйте.
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)
}
}
Это решает, что обратный удар не работает должным образом на SwiftUI, и потому что я использую имена NavigationView и NavigationLink, весь мой проект переключился на них немедленно.
пример
В примере я также показываю модальное представление.
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")
}
}
}
Редактировать: я начал с «Это кажется настолько простым, что я должен что-то упустить из виду», и я думаю, что нашел это. Похоже, что это не переводит EnvironmentObjects в следующее представление. Я не знаю, как это делает NavigationLink по умолчанию, поэтому сейчас я вручную отправляю объекты в следующее представление, где они мне нужны.
NavigationLink(destination: Text("Detail").environmentObject(objectToSendOnToTheNextView)) {
Text("Show detail")
}
Изменить 2:
Это открывает навигационный контроллер для всех представлений внутри NavigationView
делая @EnvironmentObject var nvc: UINavigationController
. Способ исправить это - сделать environmentObject, который мы используем для управления навигацией, классом fileprivate. Я исправил это в гисте: https://gist.github.com/Amzd/67bfd4b8e41ec3f179486e13e9892eeb
extension UINavigationController: ObservableObject {}
Вы можете сделать это, спустившись в UIKit и используя свой собственный UINavigationController.
Сначала создайте
SwipeNavigationController
файл:Это то же самое,
SwipeNavigationController
что и здесь , с добавлениемpushSwipeBackView()
функции.Эта функция требует,
SwipeBackHostingController
который мы определяем какЗатем мы настроили приложение
SceneDelegate
для использованияSwipeNavigationController
:Наконец, используйте его в своем
ContentView
:источник
func navController()
самом деле захватить виртуальный канал и затем нажать его - отличная идея, которая помогла мне разобраться с этой проблемой! Я отвечу на более дружелюбный ответ SwiftUI, но спасибо за вашу помощь!