自定义UIViewController转场无法交互触发问题排查
问题分析与解决方案
这问题我之前也踩过一模一样的坑!核心原因主要有两个,咱们一步步拆解清楚:
1. 交互控制器的生命周期与转场代理时机不匹配
你在手势的.began阶段才创建interactionController,但此时调用popViewController(animated: true)后,导航控制器会立刻询问它的转场代理要交互控制器。如果你的interactionController是方法内的局部变量,或者代理方法无法访问到这个实例,导航控制器就会认为没有交互转场,直接执行默认的完整动画。
关键提醒:interactionController必须定义为当前ViewController的实例属性,不能是手势方法里的局部变量!否则转场代理根本拿不到这个对象,自然没法关联交互逻辑。
2. 转场代理方法未正确实现
你需要确保导航控制器的代理(通常就是当前的ViewController)实现了UINavigationControllerDelegate的两个核心方法:一个返回自定义动画控制器,另一个返回你的交互控制器,这样导航控制器才知道要用交互驱动的转场:
// 先把interactionController定义为实例属性 private var interactionController: UIPercentDrivenInteractiveTransition? // 扩展实现导航控制器代理 extension YourViewController: UINavigationControllerDelegate { // 返回自定义动画控制器 func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { return YourCustomTransitionAnimator(operation: operation) } // 返回交互控制器,让导航控制器知道这是个交互转场 func navigationController(_ navigationController: UINavigationController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? { return interactionController } }
3. 手势方法的细节优化
调整手势处理逻辑,确保在调用pop前先创建好交互控制器,同时细化状态处理:
@objc private func handleEdgePanGesture(_ gestureRecognizer: UIScreenEdgePanGestureRecognizer) { guard let view = gestureRecognizer.view else { return } // 左边缘向右滑动时,translation.x为正,进度从0到1符合pop逻辑 let progress = gestureRecognizer.translation(in: view).x / view.frame.size.width switch gestureRecognizer.state { case .began: // 先创建交互控制器,再触发pop interactionController = UIPercentDrivenInteractiveTransition() navigationController?.popViewController(animated: true) case .changed: interactionController?.update(progress) case .ended, .cancelled: let shouldFinish = progress >= 0.5 shouldFinish ? interactionController?.finish() : interactionController?.cancel() interactionController = nil default: interactionController?.cancel() interactionController = nil } }
最后别忘了在viewDidLoad里给导航控制器设置代理,并正确添加手势:
override func viewDidLoad() { super.viewDidLoad() navigationController?.delegate = self let edgePanGesture = UIScreenEdgePanGestureRecognizer(target: self, action: #selector(handleEdgePanGesture(_:))) edgePanGesture.edges = .left // 指定左边缘触发 view.addGestureRecognizer(edgePanGesture) }
这样调整后,左边缘滑动就能触发交互式的pop转场,不会再直接跳完整动画了。
内容的提问来源于stack exchange,提问作者Can
相关产品推荐
相关产品推荐

