如何从 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 方库。
假设我们有两个 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 方库。