Acabei substituindo o padrão NavigationView
e NavigationLink
obtendo o comportamento desejado. Isso parece tão simples que devo estar ignorando algo que as visualizações SwiftUI padrão fazem?
NavigationView
Eu envolvo um UINavigationController
em um super simples UIViewControllerRepresentable
que fornece a UINavigationController
visualização de conteúdo SwiftUI como um environmentObject. Isso significa NavigationLink
que, posteriormente, é possível obter, desde que esteja no mesmo controlador de navegação (os controladores de exibição apresentados não recebem o environmentObjects), que é exatamente o que queremos.
Nota: O NavigationView precisa .edgesIgnoringSafeArea(.top)
e ainda não sei como definir isso na estrutura. Veja o exemplo se o seu nvc é cortado na parte superior.
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
Criei um NavigationLink personalizado que acessa os ambientes UINavigationController para enviar por push um UIHostingController que hospeda a próxima exibição.
Nota: Eu não implementei o selection
e isActive
que o SwiftUI.NavigationLink possui porque ainda não compreendo completamente o que eles fazem. Se você quiser ajudar com isso, comente / edite.
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)
}
}
Isso resolve o deslize traseiro que não está funcionando corretamente no SwiftUI e, como uso os nomes NavigationView e NavigationLink, todo o meu projeto mudou para esses imediatamente.
Exemplo
No exemplo, também mostro apresentação modal.
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: Comecei com "Isso parece tão simples que devo estar ignorando algo" e acho que o encontrei. Isso não parece transferir o EnvironmentObjects para a próxima exibição. Não sei como o NavigationLink padrão faz isso. Por enquanto, envio objetos manualmente para a próxima exibição onde preciso deles.
NavigationLink(destination: Text("Detail").environmentObject(objectToSendOnToTheNextView)) {
Text("Show detail")
}
Edição 2:
Isso expõe o controlador de navegação a todas as visualizações internas NavigationView
, fazendo @EnvironmentObject var nvc: UINavigationController
. A maneira de corrigir isso é tornar o environmentObject que usamos para gerenciar a navegação em uma classe privada de arquivo. Corrigi isso na essência: https://gist.github.com/Amzd/67bfd4b8e41ec3f179486e13e9892eeb