I’ve created a custom UIView that draws a UIBezierPath, assigns it to a CAShapeLayer, and then masks it to the UIView’s root layer. Now what I’m trying to do is determine if a different UIView is intersecting (or overlapping) any part of the bezier path in that custom view, using the following code:
let customView = … // define UICustomView
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")
}
The only problem is that it only returns true if the purpleView is at the top left of the partial square. The placement below returns false:
(I want it to return true if it’s overlapping any of the green, and false otherwise. I’ve only drawn 3 sides of the square and intentionally left the fill empty)
The following is the code for the custom view
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
}
}
Is there a way to make this happen? I realize using the frame.intersects() function might not be the right approach.
You are mixing up coordinate spaces. boundBoxOfPath
is in CustomView
's coordinate space, whereas purpleView.frame
is in the coordinate space of self.view
. You should only compare paths when they are in the same coordinate space. The various convert
methods in UICoordinateSpace
can be used to convert between coordinate spaces.
if let pathBounds = customView.shapeLayer.path?.boundingBoxOfPath,
self.view.convert(purpleView.frame, to: customView).intersects(pathBounds) {
print("Overlapping")
} else {
print("Not overlapping")
}
Note that this will check if the two rectangles overlap. It will return true for cases like this too:
The green "rectangle" completely covers the purple rectangle, and that is also a kind of intersection.
If you just want to check if the purple rectangle intersects the green lines, you can create a new path from the green path, such that, when filled with green, the result is the same as if you stroked the green path. See copy(strokingWithWidth:lineCap:lineJoin:miterLimit:transform:)
.
Then, you create a rectangular path from purpleView.frame
, and check if the new path and the rectangular path intersects, using CGPath.intersects
.
let purpleViewPath = CGPath(rect: self.view.convert(purpleView.frame, to: customView), transform: nil)
if let path = customView.shapeLayer.path?.copy(
strokingWithWidth: customView.shapeLayer.lineWidth,
// these are the default values for a CGShapeLayer.
// you can also try converting the values from the corresponding properties in CGShapeLayer
lineCap: .butt,
lineJoin: .miter,
miterLimit: 10
),
purpleViewPath.intersects(path, using: .evenOdd) {
print("Overlapping")
} else {
print("Not overlapping")
}