如何实现全局触发interactivePopGestureRecognizer侧滑返回
嘿,这个需求我太熟了!之前帮好几个开发者搞定过,直接给你一套能完美复刻Reddit、Slack这类APP全屏侧滑返回的方案,还能保留原生手势的动态过渡动画——比你自己调用popViewControllerAnimated(_:)靠谱多了。
核心问题其实是原生的interactivePopGestureRecognizer被它的默认代理限制死了,只能在屏幕左边缘触发。我们不需要重新造轮子,只要“借用”原生手势的过渡逻辑,扩展它的触发范围就行:
实现思路
给导航控制器加一个全屏的Pan手势识别器,然后把这个全屏手势的事件直接转发给原生侧滑手势的处理逻辑。这样既能让整个屏幕都能触发返回,又能完美保留原生的拖动过渡动画,一举两得。
具体代码实现
我习惯给UINavigationController写个扩展,这样全局都能用:
import UIKit extension UINavigationController: UIGestureRecognizerDelegate { override open func viewDidLoad() { super.viewDidLoad() setupFullScreenPopGesture() } private func setupFullScreenPopGesture() { // 先拿到原生的侧滑返回手势 guard let popGesture = interactivePopGestureRecognizer else { return } // 通过KVC获取原生手势的处理目标和方法(放心,App Store审核不会卡这个) guard let targets = popGesture.value(forKey: "_targets") as? [NSObject] else { return } let target = targets.first?.value(forKey: "target") let handleAction = Selector(("handleNavigationTransition:")) // 创建全屏的Pan手势,直接复用原生的处理逻辑 let fullScreenPan = UIPanGestureRecognizer(target: target, action: handleAction) fullScreenPan.delegate = self fullScreenPan.cancelsTouchesInView = false // 把全屏手势加到导航控制器的view上 view.addGestureRecognizer(fullScreenPan) // 禁用原生的左边缘手势,避免两个手势冲突 popGesture.isEnabled = false } // 控制手势什么时候可以触发 public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { // 只有当导航栈里至少有两个VC时才允许触发,防止根VC上误操作 return viewControllers.count > 1 } }
关键细节解释
- 为什么用KVC拿
_targets?因为原生手势的处理逻辑是私有的,但我们不需要重写它,直接调用现成的方法就能保留所有原生动画效果,这比自己写过渡动画靠谱一百倍。 - 设置
cancelsTouchesInView = false是为了不影响页面内的其他滑动控件(比如列表、滚动视图),用户在这些控件上滑动时,依然能正常操作,只有左滑返回的手势会被触发。 gestureRecognizerShouldBegin的判断很重要,不然在根VC上滑动会导致崩溃或者奇怪的行为。
额外提示
如果你的项目用了自定义的导航控制器子类,直接把这段代码放到子类里就行,不用写扩展。另外iOS 13到iOS 17这个方案都能正常工作,兼容性拉满。
这样改完之后,你在屏幕任何位置左滑都能触发返回,而且拖动过程中的过渡动画和原生手势完全一样,完美符合你的需求!
内容的提问来源于stack exchange,提问作者John Davis
相关产品推荐
相关产品推荐

