You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过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. 方案1:捏合手势默认会延迟触摸事件触发,导致handlePinch晚于touches方法执行,无法及时拦截绘图;且手动传递捏合数据很难匹配地图原生缩放行为。
  2. 方案2:hitTest会为每个触摸点单独调用,第一个触摸点触发时count为1,返回CanvasView;第二个触摸点触发时count变为2,但此时第一个触摸事件已被CanvasView捕获,无法透传。
  3. 方案3:touchesBegan中设置isUserInteractionEnabled = false时,当前触摸序列已被CanvasView捕获,后续事件不会自动透传给地图。

内容的提问来源于stack exchange,提问作者Redstain

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 21:45:24