如何从 A 中减去 UIBezierPath A&B 的交集?

How to subtract the intersection of UIBezierPath A&B from A?

假设我们有两个 UIBezierPaths,path1 和 path2...(它们已经在运行时相对于视图边界定义,并且已经作为同一视图的属性存在)。

我们要获取一个UIBezierPath类型的新路径path3,即path1减去path2的结果:

这样做的方法(如 here 所示)是这样做的:

path1.append(path2.reversing())

但是,这似乎只适用于路径 1 完全包含路径 2 的情况。

例如,考虑只有部分交叉点的情况——路径 1 没有完全包含路径 2。如果我们应用与上述相同的方法,会发生这种情况:

在Android中,答案是:

path1.op(path2, Path.Op.DIFFERENCE);

所以...在IOS中是否有等效的简单操作?

如果不是,是否有一个函数可以写成:

func returnPath2CutOutOfPath1(path1: UIBezierPath, path2: UiBezierPath) -> UIBezierPath {

// the mystery lies within these here parts. :)

}

我已经实现了您想要的解决方案,但我已经为大小为 200x200 的静态视图创建了代码。

让我向您展示我的尝试并告诉我对您有多大用处。

首先,我创建了 class 自定义视图,并在其中编写了绘制视图的代码。见以下代码:

class MyCustomView: UIView {

    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setup()
    }

    func setup() {

        // Create a CAShapeLayer
        let shapeLayer = CAShapeLayer()

        let path1 = self.path1()
        let path2 = self.path2()

        // Append path2 to path1
        path1.append(path2.reversing())

        // Set true of Even Odd Fill Rule
        path1.usesEvenOddFillRule = true

        // Call this method to add clip in path
        path1.addClip()

        shapeLayer.path = path1.cgPath

        // Apply other properties related to the path
        shapeLayer.strokeColor = UIColor.blue.cgColor
        shapeLayer.fillColor = UIColor.black.cgColor
        shapeLayer.lineWidth = 1.0
        shapeLayer.position = CGPoint(x: 0, y: 0)

        // Add the new layer to our custom view
        self.layer.addSublayer(shapeLayer)
    }

    //-----------------------------------------------------
    // This is static code as I have already told you first.
    // Create First UIBezierPath,

    func path1() -> UIBezierPath {
        let path = UIBezierPath()
        path.move(to: CGPoint(x: 0, y: 0))
        path.addLine(to: CGPoint(x: 0, y: 200))
        path.addLine(to: CGPoint(x: 200, y: 200))
        path.addLine(to: CGPoint(x: 200, y: 0))
        path.close()
        return path
    }

    // Create Second UIBezierPath
    func path2() -> UIBezierPath {
        let path = UIBezierPath()
        path.move(to: CGPoint(x: 50, y: -50))
        path.addLine(to: CGPoint(x: 50, y: 150))
        path.addLine(to: CGPoint(x: 250, y: 150))
        path.addLine(to: CGPoint(x: 250, y: -50))
        path.close()
        return path
    }
}

此自定义 class 代码将使用您在图 4 中描述的 实际 结果创建共享层。

现在要使用这个 class 我已经创建了上面 class 的实例,具有固定的高度和宽度。

override func viewDidLoad() {
    super.viewDidLoad()

    // Create a new UIView and add it to the view controller
    let myView = MyCustomView()

    // Must set clipsToBounds true to remove extra layer which are display out side of view as like your **actual** result in figure 4.
    myView.clipsToBounds = true 
    myView.frame = CGRect(x: 50, y: 100, width: 200, height: 200)
    myView.backgroundColor = UIColor.orange
    view.addSubview(myView)
}

这将显示如下视图,这是您想要的结果。

希望对您有所帮助,有任何疑问请随时联系我。

在 iOS.

上没有直接的方法来获取 UIBezierPaths 的差异作为新路径

测试用例

private func path2() -> UIBezierPath {
    return UIBezierPath(rect: CGRect(x: 100, y: 50, width: 200, height: 200))
}

private func path1() -> UIBezierPath {
    return UIBezierPath(rect: CGRect(x: 50, y: 100, width: 200, height: 200))
}

起点:简单显示哪条路径代表什么:路径 1 为黄色,路径 2 为绿色:

可能性 1

你可能已经看到了这种可能性:在你的问题中提到的link中,如果你只需要执行填充操作,也有一个巧妙的解决方案。

代码取自此答案(来自您发布的 link) - 仅转换为 Swift:

func fillDifference(path2: UIBezierPath, path1: UIBezierPath) {
    let clipPath = UIBezierPath.init(rect: .infinite)
    clipPath.append(path2)
    clipPath.usesEvenOddFillRule = true

    UIGraphicsGetCurrentContext()?.saveGState()
    clipPath.addClip()
    path1.fill()
    UIGraphicsGetCurrentContext()?.restoreGState()
}

所以这会填充路径,但不会 return UIBezierPath,这意味着您不能应用轮廓、背景、轮廓宽度等,因为结果不是 UIBezierPath。

看起来像这样:

可能性 2

您可以使用第三方库,例如来自一位名叫 Adam Wulf 的作者:https://github.com/adamwulf/ClippingBezier.

该库是用 Objective-C 编写的,但可以从 Swift 调用。

在 Swift 中,它看起来像这样:

override func draw(_ rect: CGRect) {
    let result = self.path1().difference(with: self.path2())

    for p in result ?? [] {
        p.stroke()
    }
}

如果要使用这个库,需要注意一个小提示:在项目设置中的Other Linker Flags中,如readme所述,必须添加“-ObjC++ -lstdc++”,否则会毫无怨言地构建,但会默默地不加载框架并最终崩溃,因为找不到 UIBEzierPath 类别。

结果如下所示:

所以这实际上会给出您想要的结果,但您必须使用第 3 方库。