Я закінчив переосмислення за замовчуванням NavigationView
і NavigationLink
отримати бажану поведінку. Це здається настільки простим, що я повинен не помітити те, що роблять погляди SwiftUI за замовчуванням?
NavigationView
Я загортаю дуже UINavigationController
просто, UIViewControllerRepresentable
що надає UINavigationController
перегляду вмісту SwiftUI як середовище-об'єкта. Це означає, що NavigationLink
пізніше можна схопити це, якщо він знаходиться в одному і тому ж контролері навігації (представлені контролери подання не отримують оточення "Об'єкти"), що саме те, що ми хочемо.
Примітка: 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
. Спосіб виправити це - зробити середовищеObject, яке ми використовуємо для управління навігацією, класом fileprivate. Я зафіксував це в суті: https://gist.github.com/Amzd/67bfd4b8e41ec3f179486e13e9892eeb