iOS 手势与控件事件冲突解决清单

总结一份「iOS 手势与控件事件冲突解决清单 」,以后你遇到 UIButton / UITableView / UIScrollView 被手势拦截就能快速排查了:


📌 iOS 手势与控件事件冲突常见解决办法

1️⃣ cancelsTouchesInView

👉 最常用,决定手势识别后是否取消触摸传递给子视图。

复制代码
tap.cancelsTouchesInView = NO;
  • YES (默认):手势识别成功后,子视图(按钮、cell)不会收到触摸。

  • NO:手势和子视图事件都能响应。


2️⃣ delaysTouchesBegan / delaysTouchesEnded

控制手势识别是否延迟控件事件:

复制代码
tap.delaysTouchesBegan = NO;  // 默认 NO,立即分发事件
tap.delaysTouchesEnded = NO;  // 默认 NO,不延迟结束事件
  • 常用于 UIScrollView + TapGesture 冲突,避免滚动被 tap 卡住。

3️⃣ UIGestureRecognizerDelegate

通过代理「精准控制」哪些触摸点交给手势,哪些交给控件。

复制代码
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer 
       shouldReceiveTouch:(UITouch *)touch {
    if ([touch.view isKindOfClass:[UIButton class]]) {
        return NO; // 不拦截按钮
    }
    return YES;
}

或者用坐标判断(只在某些区域才识别手势)。


4️⃣ requireGestureRecognizerToFail

让一个手势等待另一个失败之后再执行。

复制代码
[tap requireGestureRecognizerToFail:doubleTap];
  • 常见于 单击和双击冲突(比如播放器单击暂停、双击点赞)。

5️⃣ 控件自带的「手势优先级」

  • UIScrollView 本身有 pan 手势,会和自定义手势冲突。

    可用:

    复制代码
    gestureRecognizer.requireGestureRecognizerToFail(scrollView.panGestureRecognizer);
  • UITableViewCell 的选中事件属于 touch,会被 tap gesture 截获。此时用方法 1 或 3。


6️⃣ HitTest 重写 (更高级)

如果手势放在最外层,直接覆盖了内部视图,可以重写 hitTest:withEvent:,手动决定事件分发:

复制代码
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    if (CGRectContainsPoint(self.contentView.frame, point)) {
        return [super hitTest:point withEvent:event]; // 交给 contentView
    }
    return self; // 外部区域交给自己处理
}

🏷️ 常见场景速查

  • 点击空白处关闭弹窗cancelsTouchesInView = NO + delegate 区分区域

  • 单击和双击冲突requireGestureRecognizerToFail

  • ScrollView 与 Tap 冲突delaysTouchesBegan/Ended

  • 按钮点击失效shouldReceiveTouch 判断 UIButton 跳过

  • 复杂容器控件 → 重写 hitTest