iOS顶层OverlayView与下层视图同时响应触摸点击事件的实现问题
可行实现方案
你遇到的问题本质是iOS响应链默认机制:顶层视图的手势识别成功后,默认会中断事件向下层响应者的传递,以下两种方案都可以实现同时触发顶层手势追踪和下层控件响应:
方案1:手势代理实现(最简单,无需自定义View)
通过配置手势的代理方法,允许手势与下层事件同时触发:
- 让当前控制器遵守
UIGestureRecognizerDelegate协议 - 给顶层视图的手势设置代理,同时关闭手势的触摸取消属性
- 实现手势共存的代理方法
完整修改后的代码如下:
// 1. 遵守协议 class ViewController: UIViewController, UIGestureRecognizerDelegate { override func viewDidLoad() { super.viewDidLoad() let button = UIButton(frame: CGRect(x: 100, y: 100, width: 100, height: 50)) button.backgroundColor = .green button.addTarget(self, action: #selector(buttonClicked), for: .touchUpInside) self.view.addSubview(button) let topLayerView = UIView(frame: CGRect(x: 0, y: 0, width: 400, height: 900)) self.view.addSubview(topLayerView) topLayerView.backgroundColor = .yellow self.view.bringSubviewToFront(topLayerView) topLayerView.alpha = 0.1 let singleTap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(singleTap)) singleTap.numberOfTapsRequired = 1 // 2. 设置代理,关闭触摸取消属性 singleTap.delegate = self singleTap.cancelsTouchesInView = false topLayerView.addGestureRecognizer(singleTap) topLayerView.isUserInteractionEnabled = true } // 3. 实现手势共存代理方法 func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { // 允许当前overlay的手势和下层所有事件同时触发 return true } @objc func buttonClicked() { print("按钮被点击") } @objc func singleTap() { print("捕获到顶层点击事件") } }
方案2:自定义OverlayView实现(适合全量触摸事件追踪)
如果需要追踪所有类型的触摸事件(长按、滑动、捏合等所有交互),可以自定义Overlay视图,重写触摸事件方法主动透传事件:
class OverlayTrackView: UIView { // 触摸开始事件 override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { super.touchesBegan(touches, with: event) // 此处写入你要追踪的触摸开始逻辑 print("追踪到触摸开始") // 透传事件给下层响应者 next?.touchesBegan(touches, with: event) } // 触摸滑动事件 override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { super.touchesMoved(touches, with: event) // 此处写入你要追踪的滑动逻辑 print("追踪到触摸滑动") next?.touchesMoved(touches, with: event) } // 触摸结束事件 override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { super.touchesEnded(touches, with: event) // 此处写入你要追踪的触摸结束逻辑 print("追踪到触摸结束") next?.touchesEnded(touches, with: event) } // 触摸取消事件 override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) { super.touchesCancelled(touches, with: event) next?.touchesCancelled(touches, with: event) } }
使用时直接将你原来的topLayerView的类替换为OverlayTrackView即可,不需要额外添加手势就可以捕获所有交互,完全不影响下层控件的正常响应。
注意事项
- 如果你的Overlay需要在应用外悬浮展示,需要申请iOS对应的特殊权限,普通应用仅支持在自身App的Window层级内添加Overlay
- 如果需要拦截特定事件不向下传递,只需要在对应触摸方法中删除调用
next的代码即可
内容的提问来源于stack exchange,提问作者Zvone Pandza
相关产品推荐
相关产品推荐

