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

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比较,没有考虑坐标转换(比如自定义视图在父视图中的位置)。要准确判断重叠,需要做以下几步:

  1. 统一坐标系:把紫色视图的frame转换成自定义视图的本地坐标系,因为贝塞尔路径是基于自定义视图的本地坐标绘制的。
  2. 生成带线宽的路径轮廓:你的路径是带宽度的线条,实际的重叠区域是线条的轮廓范围,需要把原路径转换成包含线宽的闭合路径。
  3. 判断路径与矩形的交集:用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 05:27:09