Swift iOS:确定贝塞尔路径是否与UIView框架相交?

huangapple go评论60阅读模式
英文:

Swift iOS: Determine if bezier path intersects with UIView frame?

问题

I've translated the code portion as requested:

我创建了一个自定义的UIView,用于绘制UIBezierPath,并将其分配给CAShapeLayer,然后将其蒙版到UIView的根层。现在我试图确定不同的UIView是否与该自定义视图中的任何部分的贝塞尔路径相交(或重叠),使用以下代码:

let customView =  // 定义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("重叠")
} else {
    print("未重叠")
}

唯一的问题是,它只在紫色视图位于部分正方形的左上角时返回true。下面的放置会返回false:

(我希望它在重叠任何绿色部分时返回true,否则返回false。我只绘制了正方形的3个边,并有意留空填充)

以下是自定义视图的代码:

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.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()函数可能不是正确的方法。

英文:

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)

Swift iOS:确定贝塞尔路径是否与UIView框架相交?

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.

答案1

得分: 1

boundBoxOfPathCustomView 的坐标空间中,而 purpleView.frameself.view 的坐标空间中。只有当它们在相同的坐标空间中时,才应该比较路径。UICoordinateSpace 中的各种 convert 方法可用于在坐标空间之间进行转换。

if let pathBounds = customView.shapeLayer.path?.boundingBoxOfPath,
   self.view.convert(purpleView.frame, to: customView).intersects(pathBounds) {
    print("重叠")
} else {
    print("不重叠")
}

请注意,这将检查两个矩形是否重叠。它也将对这种情况返回 true:

Swift iOS:确定贝塞尔路径是否与UIView框架相交?

绿色 "矩形" 完全覆盖 紫色矩形,这也是一种交集。

如果您只想检查紫色矩形是否与绿色 线条 交叉,您可以从绿色路径创建一个新路径,使其在用绿色填充时的结果与您 描边 绿色路径的结果相同。参见 copy(strokingWithWidth:lineCap:lineJoin:miterLimit:transform:)

然后,您可以从 purpleView.frame 创建一个矩形路径,并使用 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,

    // 这些是 CGShapeLayer 的默认值。
    // 您也可以尝试从 CGShapeLayer 中的相应属性转换这些值
    lineCap: .butt,
    lineJoin: .miter,
    miterLimit: 10
),
   purpleViewPath.intersects(path, using: .evenOdd) {
    print("重叠")
} else {
    print("不重叠")
}
英文:

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:

Swift iOS:确定贝塞尔路径是否与UIView框架相交?

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")
}

huangapple
  • 本文由 发表于 2023年5月21日 14:30:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/76298585.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定