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

UIScrollView手势冲突:touchesBegan偶现不触发致绘制时页面滚动

问题根因

核心问题是UIScrollView的内置平移手势存在事件拦截优先级:系统会给pan手势预留一段识别判定窗口,在窗口期间触摸事件不会传递到下层子视图,你写在子视图touchesBegan里的禁用滚动逻辑存在时序滞后,无法覆盖手势识别的抢跑阶段。
偶现的触发逻辑非常明确:

  • 若触摸后停留时间较长、初始移动位移未达到scrollView的滚动触发阈值,pan手势识别失败,事件才会下发到子视图触发touchesBegan,此时禁用滚动的逻辑正常执行,画笔可以正常绘制
  • 若触摸后短时间内产生滑动位移,pan手势会在子视图接收到事件前识别成功,直接触发滚动逻辑,子视图的touchesBegan完全不会被调用,禁用滚动的代码自然不会执行,就会出现滑动打断绘制的异常
可行解决方案
  • 最优方案:工具切换时直接修改scrollView状态,不要等触摸触发再调整
    当用户选中画笔、橡皮擦工具时,立刻执行scrollView.isScrollEnabled = false、scrollView.panGestureRecognizer.isEnabled = false;当用户切回浏览、选择等非绘制工具时,再重新开启scrollView的滚动能力,从根源上消除手势抢事件的时序问题。
  • 方案2:通过手势代理调整识别优先级
    不要依赖touchesBegan系列方法处理绘制逻辑,给Page视图绑定自定义的UIPanGestureRecognizer专门处理绘制,实现UIGestureRecognizerDelegate协议控制手势优先级:
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool {
    guard let pagePan = gestureRecognizer as? UIPanGestureRecognizer,
          pagePan.view is Page,
          (currentTool == .PEN || currentTool == .ERASER),
          otherGestureRecognizer == scrollView.panGestureRecognizer else {
        return false
    }
    // 绘制场景下,要求scrollView的滚动手势必须等绘制手势识别失败后才能触发
    return true
}
  • 方案3:重写hitTest拦截事件传递
    自定义UIScrollView子类,重写hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView?方法,当触摸点落在Page视图上、且当前处于绘制工具模式时,直接返回对应的Page视图,阻断scrollView对事件的拦截。

内容的提问来源于stack exchange,提问作者KindestPath0b

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 07:21:20