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

如何阻止触摸事件传递至下层视图?UIScrollView手势冲突问题

解决方案

方法一:在ScrollView触摸方法中过滤子视图区域的触摸

重写自定义UIScrollView的touchesBegan、touchesMoved等方法,先判断触摸点是否落在带有UIPanGestureRecognizer的子视图范围内,若是则跳过所有自定义处理逻辑(也可根据需求选择是否调用super方法)。

步骤:

  1. 添加辅助方法判断触摸点是否在目标子视图上:
private func isTouchOnPanEnabledSubview(_ touch: UITouch) -> Bool {
    let touchLocation = touch.location(in: self)
    // 遍历子视图,检查是否包含UIPan手势且触摸点在其范围内
    for subview in subviews {
        guard let gestures = subview.gestureRecognizers else { continue }
        let hasPanGesture = gestures.contains(where: { $0 is UIPanGestureRecognizer })
        guard hasPanGesture else { continue }
        
        let locationInSubview = touch.location(in: subview)
        if subview.point(inside: locationInSubview, with: nil) {
            return true
        }
    }
    return false
}
  1. 在重写的触摸方法中做过滤:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    guard let touch = touches.first, !isTouchOnPanEnabledSubview(touch) else {
        // 触摸在子视图上时直接返回,不执行任何自定义逻辑
        return
    }
    // 你的自定义触摸处理逻辑
    // ...
}

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    guard let touch = touches.first, !isTouchOnPanEnabledSubview(touch) else {
        return
    }
    // 你的自定义触摸处理逻辑
    // ...
}

方法二:利用手势识别器代理拦截事件传递

给子视图的UIPanGestureRecognizer设置代理,结合delaysTouchesBegan属性延迟向ScrollView传递触摸事件,让手势优先完成识别判断。

步骤:

  1. 配置子视图的Pan手势:
// 子视图初始化或配置阶段
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
panGesture.cancelsTouchesInView = true
panGesture.delaysTouchesBegan = true // 关键:延迟向视图传递触摸事件,直到手势确定是否识别
panGesture.delegate = self
subview.addGestureRecognizer(panGesture)
  1. 实现手势代理方法:
extension YourViewController: UIGestureRecognizerDelegate {
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        // 阻止与ScrollView原生手势同时识别
        if otherGestureRecognizer.view is UIScrollView {
            return false
        }
        return true
    }
    
    func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        // 确保只有触摸在子视图范围内时,手势才启动识别
        let touchPoint = gestureRecognizer.location(in: gestureRecognizer.view)
        return gestureRecognizer.view?.point(inside: touchPoint, with: nil) ?? false
    }
}

方法三:重写ScrollView的hitTest方法优化事件路径

通过重写hitTest方法,直接返回带有Pan手势的子视图作为第一响应者,减少ScrollView接收触摸事件的概率,需配合方法一使用以完全屏蔽自定义逻辑。

override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
    let hitView = super.hitTest(point, with: event)
    // 如果命中带Pan手势的子视图,直接返回它作为第一响应者
    if let hitView = hitView, hitView.gestureRecognizers?.contains(where: { $0 is UIPanGestureRecognizer }) == true {
        return hitView
    }
    return super.hitTest(point, with: event)
}

注意事项

  • 方法一最直接,可完全屏蔽ScrollView在子视图区域的触摸逻辑执行;
  • 方法二通过手势延迟和代理,减少ScrollView接收到的触摸事件次数;
  • 方法三优化事件传递路径,但无法完全阻止ScrollView收到初始触摸事件,需配合方法一使用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 05:08:09