私はデフォルトNavigationView
をオーバーライドNavigationLink
して、望ましい動作を得ることになりました。これは単純すぎるようですが、デフォルトのSwiftUIビューが行うことを見落としている必要がありますか?
NavigationView
私はSwiftUIコンテンツビューをenvironmentObjectとして提供UINavigationController
する非常にシンプルなでラップします。これは、同じナビゲーションコントローラー内にある限り(表示されたビューコントローラーがenvironmentObjectsを受信しない限り)、後でそれを取得できることを意味します。UIViewControllerRepresentable
UINavigationController
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
次のビューをホストしているUIHostingControllerをプッシュするために環境UINavigationControllerにアクセスするカスタムNavigationLinkを作成します。
注:私は実装していなかった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