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

时间:2018-12-15 06:46:35

标签: ios swift vector drawing uibezierpath

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

我们要获取类型为UIBezierPath的新路径path3,这是从path1中减去path2的结果:

enter image description here

完成此操作的方式(如here所示)是

path1.append(path2.reversing())

但是,这似乎仅在path1完全包含path2的情况下有效。

例如,考虑只有部分相交的情况-path1不完全包含path2。如果我们采用与上述相同的方法,则会发生这种情况:

enter image description here

在Android中,答案是:

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

那么... IOS中是否有等效的简单操作?

如果没有,是否存在可以写为的函数:

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

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

}

2 个答案:

答案 0 :(得分:2)

我已经实现了所需的解决方案,但是我已经为200x200尺寸的静态视图创建了代码。

让我告诉您我尝试过的事情,并告诉我对您有多有用。

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

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

此自定义类代码将根据您的实际结果创建共享层,如图4所示。

现在要使用此类,我已经创建了具有固定高度和宽度的上述类的实例。

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

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

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

答案 1 :(得分:1)

没有直接的方法可以将UIBezierPaths的差异作为iOS上的新路径。

测试用例

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为绿色:

starting point

可能性1

您可能已经看到了这种可能性:在问题中提到的链接中,如果只需要执行填充操作,还有一个聪明的解决方案。

代码是从此答案中提取的(来自您发布的链接)https://stackoverflow.com/a/8860341-仅转换为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()
}

因此,这将填充路径,但不会返回UIBezierPath,这意味着您不能应用轮廓,背景,轮廓宽度等,因为结果不是UIBezierPath。

它看起来像这样:

fill only

可能性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()
    }
}

如果要使用此库,则必须注意一个小提示:如自述文件所述,在项目设置的“其他链接器标志”中,必须添加“ -ObjC ++ -lstdc ++”,否则将在没有投诉,但由于没有找到UIBEzierPath类别,因此将无提示地不加载框架并最终崩溃。

结果如下:

real bezier path result

因此,这实际上将提供您想要的结果,但是您将必须使用第3方库。