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