總結一份「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