我查阅了有关这个问题的文档和其他来源,但并没有找到任何信息。只有一些解决方案,基于使用UIKit
和UIViewControllerRepresentable
。我尝试结合这个问题的解决方案,并成功将返回手势与替换后退按钮的其他视图结合起来。代码还有点混乱,但我认为这是进一步发展的起点(例如完全隐藏导航栏)。下面是ContentView
的代码:
import SwiftUI
struct ContentView: View {
var body: some View {
SwipeBackNavController {
SwipeBackNavigationLink(destination: DetailViewWithCustomBackButton()) {
Text("Main view")
}
.navigationBarTitle("Standard SwiftUI nav view")
}
.edgesIgnoringSafeArea(.top)
}
}
struct DetailViewWithCustomBackButton: View {
@Environment(\.presentationMode) var presentationMode
var body: some View {
Text("detail")
.navigationBarItems(leading: Button(action: {
self.dismissView()
}) {
HStack {
Image(systemName: "return")
Text("Back")
}
})
.navigationBarTitle("Detailed view")
}
private func dismissView() {
presentationMode.wrappedValue.dismiss()
}
}
这里是
SwipeBackNavController
和
SwipeBackNavigationLink
的实现,它们类似于
NavigationView
和
NavigationLink
。它们只是对
SwipeNavigationController
的包装。最后一个是
UINavigationController
的子类,可以根据您的需要进行定制:
import UIKit
import SwiftUI
struct SwipeBackNavController<Content: View>: UIViewControllerRepresentable {
let content: Content
public init(@ViewBuilder content: @escaping () -> Content) {
self.content = content()
}
func makeUIViewController(context: Context) -> SwipeNavigationController {
let hostingController = UIHostingController(rootView: content)
let swipeBackNavController = SwipeNavigationController(rootViewController: hostingController)
return swipeBackNavController
}
func updateUIViewController(_ pageViewController: SwipeNavigationController, context: Context) {
}
}
struct SwipeBackNavigationLink<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
}
var body: some View {
Button(action: {
guard let window = UIApplication.shared.windows.first else { return }
guard let swipeBackNavController = window.rootViewController?.children.first as? SwipeNavigationController else { return }
swipeBackNavController.pushSwipeBackView(DetailViewWithCustomBackButton())
}, label: label)
}
}
final class SwipeNavigationController: UINavigationController {
override init(rootViewController: UIViewController) {
super.init(rootViewController: rootViewController)
}
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
delegate = self
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
delegate = self
}
override func viewDidLoad() {
super.viewDidLoad()
interactivePopGestureRecognizer?.delegate = self
}
deinit {
delegate = nil
interactivePopGestureRecognizer?.delegate = nil
}
override func pushViewController(_ viewController: UIViewController, animated: Bool) {
duringPushAnimation = true
setNavigationBarHidden(true, animated: false)
super.pushViewController(viewController, animated: animated)
}
var duringPushAnimation = false
func pushSwipeBackView<Content>(_ content: Content) where Content: View {
let hostingController = SwipeBackHostingController(rootView: content)
self.delegate = hostingController
self.pushViewController(hostingController, animated: true)
}
}
extension SwipeNavigationController: UINavigationControllerDelegate {
func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
guard let swipeNavigationController = navigationController as? SwipeNavigationController else { return }
swipeNavigationController.duringPushAnimation = false
}
}
extension SwipeNavigationController: UIGestureRecognizerDelegate {
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
guard gestureRecognizer == interactivePopGestureRecognizer else {
return true
}
let result = viewControllers.count > 1 && duringPushAnimation == false
return result
}
}
class SwipeBackHostingController<Content: View>: UIHostingController<Content>, UINavigationControllerDelegate {
func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
guard let swipeNavigationController = navigationController as? SwipeNavigationController else { return }
swipeNavigationController.duringPushAnimation = false
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
guard let swipeNavigationController = navigationController as? SwipeNavigationController else { return }
swipeNavigationController.delegate = nil
}
}
这个实现提供了保存自定义的返回按钮和滑动返回手势的功能。我仍然不喜欢一些细节,比如SwipeBackNavigationLink
是如何推送视图的,所以后面我会继续研究。
viewControllers.count > 1
是什么意思?再次感谢。 - Merunas Grincalaitis