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
相关产品推荐
相关产品推荐

