Swift iOS:如何判断UIBezierPath与UIView框架是否相交?
需求:判断UIView与自定义视图中贝塞尔路径的重叠情况
我创建了一个自定义UIView,绘制UIBezierPath并赋值给CAShapeLayer,再将该图层作为UIView根图层的遮罩。现在需要判断另一个UIView是否与自定义视图中的贝塞尔路径任意部分重叠,我用了以下代码:
let customView = … // 实例化CustomView let purpleView = UIView(frame: CGRect(x: 30, y: 500, width: 100, height: 50)) purpleView.backgroundColor = UIColor.purple self.view.addSubview(purpleView) if purpleView.frame.intersects((customView.shapeLayer.path?.boundingBoxOfPath)!) == true { print("Overlapping") } else { print("Not overlapping") }
但问题是只有当紫色视图位于部分正方形的左上角时才返回true,其他位置(比如下方)会返回false。我希望只要紫色视图与自定义视图中任何绿色线条部分重叠就返回true,否则返回false。自定义视图只绘制了正方形的三边,故意没有闭合路径填充。
自定义视图的代码如下:
class CustomView : UIView { private var _path : UIBezierPath! public let shapeLayer = CAShapeLayer() override init(frame: CGRect) { super.init(frame: frame) self._setup() } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) self._setup() } private func _setup() { self._createSquare() } private func _createSquare() { self._path = UIBezierPath() self._path.move(to: CGPoint(x: 0.0, y: 0.0)) self._path.addLine(to: CGPoint(x: 0.0, y: self.frame.size.height)) self._path.addLine(to: CGPoint(x: self.frame.size.width, y: self.frame.size.height)) self._path.addLine(to: CGPoint(x: self.frame.size.width, y: 0.0)) //self._path.close() self.shapeLayer.path = self._path.cgPath self.shapeLayer.lineWidth = 10 self.shapeLayer.strokeColor = UIColor.green.cgColor self.backgroundColor = UIColor.green self.shapeLayer.fillColor = nil self.layer.mask = self.shapeLayer } }
我意识到用frame.intersects()不是正确的方法,请问该如何实现需求?
解决方案
你的问题核心在于:boundingBoxOfPath只是路径的最小外接矩形,没法代表带线宽的实际绘制区域;而且直接用视图的frame比较,没有考虑坐标转换(比如自定义视图在父视图中的位置)。要准确判断重叠,需要做以下几步:
- 统一坐标系:把紫色视图的frame转换成自定义视图的本地坐标系,因为贝塞尔路径是基于自定义视图的本地坐标绘制的。
- 生成带线宽的路径轮廓:你的路径是带宽度的线条,实际的重叠区域是线条的轮廓范围,需要把原路径转换成包含线宽的闭合路径。
- 判断路径与矩形的交集:用CoreGraphics的API判断转换后的矩形是否与带线宽的路径有交集。
具体实现代码如下:
// 1. 将紫色视图的区域转换到自定义视图的本地坐标 let purpleRectInCustomView = customView.convert(purpleView.bounds, from: purpleView) // 2. 生成包含线宽的路径轮廓(模拟线条实际绘制的区域) guard let originalPath = customView.shapeLayer.path else { return } let strokePath = originalPath.copy(strokingWithWidth: customView.shapeLayer.lineWidth, lineCap: customView.shapeLayer.lineCap, lineJoin: customView.shapeLayer.lineJoin, miterLimit: customView.shapeLayer.miterLimit) // 3. 判断矩形与路径是否存在重叠 let isOverlapping = strokePath.intersects(purpleRectInCustomView) if isOverlapping { print("Overlapping") } else { print("Not overlapping") }
代码说明:
convert(_:from:):解决视图层级不同导致的坐标偏差问题,确保两个区域的坐标统一到自定义视图的坐标系下。copy(strokingWithWidth:...):将原线条路径转换为代表线条实际绘制范围的闭合路径,这样就能准确覆盖所有绿色线条的区域。intersects(_:):CoreGraphics提供的原生方法,能精准检测路径与矩形是否存在重叠部分。
另外需要注意:如果自定义视图的frame发生变化(比如使用AutoLayout自适应布局),要重新计算贝塞尔路径,否则路径的坐标会与视图实际位置不匹配。
内容的提问来源于stack exchange,提问作者user4496950
相关产品推荐
相关产品推荐

