iOS设置cancelsTouchesInView=false后子视图按钮手势冲突问题
解决UIGestureRecognizer与自定义AlertView控件冲突的问题
这个问题我之前也碰到过,核心原因是虽然你把cancelsTouchesInView设为false,但手势识别器还是会接收到AlertView子控件的触摸事件,进而触发响应,干扰了按钮的点击逻辑。下面给你两种靠谱的解决思路:
方法一:利用手势识别器的Delegate过滤触摸事件
这是最直接有效的方案,通过手势的代理方法,让它主动忽略AlertView内部所有控件的触摸:
- 首先让你的ViewController遵守
UIGestureRecognizerDelegate协议:
class YourViewController: UIViewController, UIGestureRecognizerDelegate { // ... 你的现有代码 }
- 实现
gestureRecognizer(_:shouldReceive:)方法,判断触摸的视图是否属于AlertView的子控件,如果是就不让手势处理:
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { // 假设你的AlertView实例叫alertView,替换成你实际的变量名 guard let touchedView = touch.view else { return true } // 判断触摸点所在的视图是否是alertView的后代控件 if touchedView.isDescendant(of: alertView) { return false // 不让手势处理这个触摸 } return true // 其他情况正常响应手势 }
- 别忘了把你的手势识别器的delegate设为当前ViewController:
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap)) tapGesture.delegate = self tapGesture.cancelsTouchesInView = false view.addGestureRecognizer(tapGesture)
这样设置后,当点击AlertView里的按钮或文本框时,手势识别器会直接跳过这些触摸,控件就能正常响应自己的事件了。
方法二:在自定义AlertView内部处理事件优先级
如果你的AlertView是自定义的类,也可以在它内部做处理,确保子控件的触摸事件优先被响应:
在AlertView类里重写hitTest(_:with:)方法,优先返回可交互的子控件(比如按钮):
class AlertView: UIView { override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { let hitView = super.hitTest(point, with: event) // 如果点击的是可交互的子控件,直接返回它,让它处理事件 if let hitView = hitView, hitView is UIControl { return hitView } return super.hitTest(point, with: event) } }
不过这种方法不如第一种灵活,推荐优先用Delegate的方式,因为它不需要修改AlertView的代码,更适合现有项目的快速调整。
内容的提问来源于stack exchange,提问作者DJ-Glock
相关产品推荐
相关产品推荐

