自定义全屏侧滑返回取消后SwiftUI视图交互失效如何解决?
问题根因
这个问题是自定义交互转场和UIHostingController的视图事件传递机制不兼容导致的:
- 自定义pop转场取消时,UIKit的转场上下文未正确将视图响应权交还给SwiftUI层,事件传递链被阻断
- 重复添加的侧滑手势和SwiftUI内置手势存在响应冲突
- 自定义转场动画器缺少取消场景的状态清理逻辑
最优解决方案:复用系统侧滑手势扩展全屏响应
不需要自己实现整套交互转场逻辑,直接复用系统自带的interactivePopGestureRecognizer,扩展其响应范围到全屏,天然兼容SwiftUI,不会出现取消后无响应的问题:
class BaseNavigationController: UINavigationController, UIGestureRecognizerDelegate { override func viewDidLoad() { super.viewDidLoad() setupFullScreenPopGesture() } private func setupFullScreenPopGesture() { // 获取系统侧滑手势的内部target guard let interactivePopGesture = interactivePopGestureRecognizer, let targets = interactivePopGesture.value(forKey: "targets") as? [NSObject], let target = targets.first?.value(forKey: "target") else { return } // 复用系统侧滑的action let action = Selector(("handleNavigationTransition:")) // 创建全屏pan手势,使用和系统侧滑相同的target和action let fullScreenPan = UIPanGestureRecognizer(target: target, action: action) fullScreenPan.delegate = self view.addGestureRecognizer(fullScreenPan) // 禁用原系统边缘侧滑手势 interactivePopGesture.isEnabled = false } // 手势触发条件判断 func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { // 根视图控制器不触发侧滑 guard viewControllers.count > 1 else { return false } // 只响应右滑 if let pan = gestureRecognizer as? UIPanGestureRecognizer { let translation = pan.translation(in: view) return translation.x > 0 } return true } }
自定义转场场景的修复方案
如果一定要保留自定义的转场动画,按以下步骤修改原有代码即可修复问题:
- 避免重复添加手势,给视图控制器增加已添加手势标记
// 关联对象key private var popGestureAddedKey: UInt8 = 0 private func addPanGesture(_ viewController: UIViewController) { if objc_getAssociatedObject(viewController, &popGestureAddedKey) as? Bool == true { return } let popRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handlePanRecognizer(recognizer:))) popRecognizer.delegate = self viewController.view.addGestureRecognizer(popRecognizer) objc_setAssociatedObject(viewController, &popGestureAddedKey, true, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) }
- 交互转场取消时强制修复UIHostingController的响应状态,修改
handlePanRecognizer的结束分支:
@objc func handlePanRecognizer(recognizer: UIPanGestureRecognizer){ var progress = recognizer.translation(in: self.view).x / self.view.bounds.size.width progress = min(1, max(0, progress)) if recognizer.state == .began { self.interactivePopTransition = UIPercentDrivenInteractiveTransition() self.popViewController(animated: true) }else if recognizer.state == .changed { interactivePopTransition.update(progress) }else if recognizer.state == .ended || recognizer.state == .cancelled { if progress > 0.2 { interactivePopTransition.finish() }else{ interactivePopTransition.cancel() // 转场取消后修复SwiftUI响应状态 DispatchQueue.main.async { [weak self] in guard let self = self, let topVC = self.topViewController else { return } topVC.view.isUserInteractionEnabled = false topVC.view.isUserInteractionEnabled = true topVC.view.superview?.bringSubviewToFront(topVC.view) } } interactivePopTransition = nil } }
- 完善自定义
NavigationPopTransition的收尾逻辑,在动画结束后正确调用completeTransition:
class NavigationPopTransition: NSObject, UIViewControllerAnimatedTransitioning { func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return 0.3 } func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { guard let fromVC = transitionContext.viewController(forKey: .from), let toVC = transitionContext.viewController(forKey: .to), let containerView = transitionContext.containerView as? UIView else { return } // 添加toVC到容器 containerView.insertSubview(toVC.view, belowSubview: fromVC.view) toVC.view.frame = CGRect(x: -containerView.bounds.width * 0.3, y: 0, width: containerView.bounds.width, height: containerView.bounds.height) UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: { fromVC.view.frame = CGRect(x: containerView.bounds.width, y: 0, width: containerView.bounds.width, height: containerView.bounds.height) toVC.view.frame = containerView.bounds }) { finished in // 必须调用completeTransition通知系统转场结束 transitionContext.completeTransition(!transitionContext.transitionWasCancelled) } } }
内容的提问来源于stack exchange,提问作者hemo
相关产品推荐
相关产品推荐

