You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 22:09:03