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

Swift中点击多个CAShapeLayer仅能选中最后一个该如何解决?

问题根源

你所有添加的CAShapeLayer的frame都被设置为了backgroundIV.bounds,大小完全一致。CALayer默认的hitTest逻辑只会判断点击点是否落在layer的bounds范围内,不会感知layer内部的贝塞尔路径形状,所以永远返回层级最靠上(最后添加)的图层,不管你点击的位置对应哪个路径。

解决方法

修改触摸事件的判断逻辑,遍历backgroundIV的所有子图层,手动判断点击点是否落在对应CAShapeLayer的路径范围内:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    super.touchesBegan(touches, with: event)
    guard let point = touches.first?.location(in: self.backgroundIV) else { return }
    
    // 倒序遍历图层(从上到下,优先命中上层显示的路径,符合视觉交互逻辑)
    for layer in self.backgroundIV.layer.sublayers?.reversed() ?? [] {
        guard let shapeLayer = layer as? CAShapeLayer,
              let path = shapeLayer.path else { continue }
        
        // 把点击点转换为shapeLayer自身坐标系的点,匹配path的坐标基准
        let layerPoint = self.backgroundIV.layer.convert(point, to: shapeLayer)
        
        var isHit = false
        // 判断是否命中填充区域
        if shapeLayer.fillColor != nil, path.contains(layerPoint) {
            isHit = true
        }
        // 判断是否命中描边区域
        else if shapeLayer.strokeColor != nil {
            // 生成对应描边宽度的路径,判断点是否在描边范围内
            let strokePath = path.copy(strokingWithWidth: shapeLayer.lineWidth, lineCap: shapeLayer.lineCap, lineJoin: shapeLayer.lineJoin, miterLimit: shapeLayer.miterLimit)
            if strokePath.contains(layerPoint) {
                isHit = true
            }
        }
        
        if isHit {
            selectedLayer = shapeLayer
            shapeLayer.strokeColor = UIColor.red.cgColor
            shapeLayer.lineWidth = 3
            print("Touched layer")
            break
        }
    }
}
  • 可选优化:如果图层数量很多,可以提前把不需要响应点击的layer的isUserInteractionEnabled设为false,遍历的时候直接跳过该类图层,减少判断开销。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 09:15:02