如何通过UIPanGesture实现UIViewController推入导航栈的拖拽交互
实现拖拽手势从右侧推入视图控制器到导航栈的方案
目前没有系统原生API直接支持拖拽push交互,需要通过自定义导航控制器转场的方式实现,核心逻辑如下:
核心实现逻辑
- 自定义导航控制器并设置其代理,遵循
UINavigationControllerDelegate协议,同时持有UIPercentDrivenInteractiveTransition类型的交互控制器实例,用来控制转场进度 - 给导航控制器的view添加自定义
UIPanGestureRecognizer拖拽手势,需要实现手势代理方法处理和系统自带pop手势的冲突,避免响应异常 - 手势触发判定:当手势起始位置落在你设定的触发区域(一般是屏幕右边缘10~20pt范围内),且当前栈顶控制器支持触发push操作时,启动自定义交互转场
- 进度计算:拖拽过程中用x轴偏移量除以屏幕宽度(或者自定义的阈值,一般取屏幕宽度的0.5)得到当前转场完成百分比,调用交互控制器的
update(_ percentComplete: CGFloat)方法更新转场进度 - 结束状态处理:手势结束时如果完成百分比超过阈值、或者横向拖拽速度足够快,就调用
finish()方法完成push转场,否则调用cancel()方法取消转场,页面回弹到初始状态
关键实现代码示例
// 导航控制器代理核心实现 extension CustomNavController: UINavigationControllerDelegate { func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { // 交互push场景返回自定义动画器 if operation == .push, isInteractivePushTriggered { return InteractivePushAnimator() } return nil } func navigationController(_ navigationController: UINavigationController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? { return isInteractivePushTriggered ? percentDrivenTransition : nil } } // 拖拽手势处理核心逻辑 @objc private func handlePanGesture(_ gesture: UIPanGestureRecognizer) { let translationX = gesture.translation(in: view).x // 向左拖拽为正方向,计算完成百分比 let progress = max(0, min(1, -translationX / view.bounds.width)) switch gesture.state { case .began: isInteractivePushTriggered = true // 初始化目标控制器并执行push let targetVC = YourTargetViewController() pushViewController(targetVC, animated: true) case .changed: percentDrivenTransition.update(progress) case .ended, .cancelled: isInteractivePushTriggered = false // 进度超过0.5或者速度超过阈值就完成转场 if progress > 0.5 || gesture.velocity(in: view).x < -300 { percentDrivenTransition.finish() } else { percentDrivenTransition.cancel() } default: break } }
注意事项
- 自定义
InteractivePushAnimator要实现UIViewControllerAnimatedTransitioning协议,动画逻辑和系统pop交互镜像对齐:旧页面向左偏移+小幅缩放(一般缩放到0.97),新页面从右侧完全进入,同时添加渐变阴影层,保证视觉效果和系统pop完全一致 - 动画时长、阻尼系数、速度曲线要和系统转场参数对齐,一般动画时长设为0.3秒,保证push和pop的交互手感统一
- 转场过程中要禁止导航栏的用户交互,避免用户在转场过程中点击返回按钮导致状态异常
内容的提问来源于stack exchange,提问作者rabus mccaleb
相关产品推荐
相关产品推荐

