如何通过HitTest与UIGestureRecognizer区分视图交互响应?
解决CanvasView与GMSMapView的手势冲突问题
针对你提出的「单指在CanvasView绘图、双指捏合让下方GMSMapView缩放」的需求,以下是两种可行的解决方案:
方案1:通过point(inside:with:)控制触摸透传
在自定义的CanvasView中重写point(inside:with:)方法,根据触摸数量决定是否接收触摸事件,直接实现事件透传:
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { // 双指及以上触摸时,拒绝接收事件,让事件自动传递给下方的GMSMapView if let touches = event?.allTouches, touches.count > 1 { return false } // 单指触摸时正常接收,执行绘图逻辑 return super.point(inside: point, with: event) }
同时在触摸方法中增加防护,避免触摸数变化时出现异常:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { guard touches.count == 1 else { super.touchesBegan(touches, with: event) return } // 这里写你原有的单指绘图逻辑 } override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { guard touches.count == 1 else { super.touchesMoved(touches, with: event) return } // 这里写你原有的单指绘图逻辑 } override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { guard touches.count == 1 else { super.touchesEnded(touches, with: event) return } // 这里写你原有的单指绘图逻辑 }
这种方式最直接,当用户双指操作时,CanvasView会自动放弃触摸事件,让GMSMapView响应捏合缩放;单指操作时正常绘图。
方案2:结合捏合手势与代理控制
如果方案1不生效,可以通过手势识别+代理的方式,手动控制交互状态:
class CanvasView: UIImageView, UIGestureRecognizerDelegate { private var isPinching = false override init(frame: CGRect) { super.init(frame: frame) setupPinchGesture() } required init?(coder: NSCoder) { super.init(coder: coder) setupPinchGesture() } private func setupPinchGesture() { let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(handlePinch)) pinchGesture.delegate = self // 关闭手势延迟,确保捏合触发时能及时响应 pinchGesture.delaysTouchesBegan = false pinchGesture.delaysTouchesEnded = false addGestureRecognizer(pinchGesture) } @objc private func handlePinch(_ sender: UIPinchGestureRecognizer) { switch sender.state { case .began, .changed: isPinching = true // 禁用CanvasView交互,让下方地图接收捏合事件 isUserInteractionEnabled = false case .ended, .cancelled, .failed: isPinching = false // 恢复交互,继续支持绘图 isUserInteractionEnabled = true default: break } } // 允许手势与原生触摸事件同时识别 func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { return true } // 触摸方法中判断是否处于捏合状态 override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { guard !isPinching, touches.count == 1 else { super.touchesBegan(touches, with: event) return } // 原有的绘图逻辑 } override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { guard !isPinching, touches.count == 1 else { super.touchesMoved(touches, with: event) return } // 原有的绘图逻辑 } override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { guard !isPinching, touches.count == 1 else { super.touchesEnded(touches, with: event) return } // 原有的绘图逻辑 } }
捏合开始时禁用CanvasView交互,让地图响应缩放;捏合结束后恢复交互,继续支持单指绘图。
你之前方案失效的原因
- 方案1:捏合手势默认会延迟触摸事件触发,导致
handlePinch晚于touches方法执行,无法及时拦截绘图;且手动传递捏合数据很难匹配地图原生缩放行为。 - 方案2:
hitTest会为每个触摸点单独调用,第一个触摸点触发时count为1,返回CanvasView;第二个触摸点触发时count变为2,但此时第一个触摸事件已被CanvasView捕获,无法透传。 - 方案3:
touchesBegan中设置isUserInteractionEnabled = false时,当前触摸序列已被CanvasView捕获,后续事件不会自动透传给地图。
内容的提问来源于stack exchange,提问作者Redstain
相关产品推荐
相关产品推荐

