如何在 CAShapeLayer 上只填充形状的一部分
How to fill only a part of the shape on CAShapeLayer
我正在使用 UIBezierPath 创建以下形状,然后使用 CAShapeLayer 绘制它,如下所示。
然后我可以将 CAShapeLayer fillColor 属性 从 shapeLayer.fillColor = UIColor.clear.cgColor
更改为 shapeLayer.fillColor = UIColor.blue.cgColor
并获得以下形状。
代码如下:
import UIKit
class ViewController: UIViewController {
var line = [CGPoint]()
let bezierPath: UIBezierPath = UIBezierPath()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
let point1 = CGPoint(x: 100, y: 100)
let point2 = CGPoint(x: 400, y: 100)
line.append(point1)
line.append(point2)
addCircle(toRight: false)
addLine()
addCircle(toRight: true)
let shapeLayer = CAShapeLayer()
shapeLayer.path = bezierPath.cgPath
shapeLayer.strokeColor = UIColor.blue.cgColor
shapeLayer.fillColor = UIColor.clear.cgColor
self.view.layer.addSublayer(shapeLayer)
}
func addLine() -> Void {
bezierPath.move(to: line.first!)
bezierPath.addLine(to: line.last!)
}
func addCircle(toRight: Bool) -> Void {
let angle = CGFloat( Double.pi / 180 )
let r = CGFloat(20.0)
let x0 = toRight ? line.first!.x : line.last!.x
let y0 = toRight ? line.first!.y : line.last!.y
let x1 = toRight ? line.last!.x : line.first!.x
let y1 = toRight ? line.last!.y : line.first!.y
let x = x1 - x0
let y = y1 - y0
let h = (x*x + y*y).squareRoot()
let x2 = x0 + (x * (h + r) / h)
let y2 = y0 + (y * (h + r) / h)
// Add the arc, starting at that same point
let point2 = CGPoint(x: x2, y: y2)
let pointZeroDeg = CGPoint(x: x2 + r, y: y2)
self.bezierPath.move(to: pointZeroDeg)
self.bezierPath.addArc(withCenter: point2, radius: r,
startAngle: 0*angle, endAngle: 360*angle,
clockwise: true)
self.bezierPath.close()
}
}
但我其实想要的是下面的形状(左边的圆是填充的,右边的圆是空的)。
所以我的问题是,如何在 CAShapeLayer 上只填充形状的一部分?
可能吗?有什么技巧可以实现吗?
PS: 我可以通过创建 3 个不同的 UIBezierPaths(用于 leftCircle、line 和 rightCircle)并使用 3 个不同的 CAShapeLayers 绘制它们来实现这一点,如下所示。
// left Circle
shapeLayer1.path = bezierPath1.cgPath
shapeLayer1.fillColor = UIColor.blue.cgColor
// line
shapeLayer2.path = bezierPath2.cgPath
// right Circle
shapeLayer3.path = bezierPath3.cgPath
shapeLayer3.fillColor = UIColor.clear.cgColor
self.view.layer.addSublayer(shapeLayer1)
self.view.layer.addSublayer(shapeLayer2)
self.view.layer.addSublayer(shapeLayer3)
但我更喜欢使用单个 CAShapeLayer 来实现这一点。
我认为更好的方法是使用多种形状。
然而,您可以使用 evenOdd
形状填充规则来实现单一形状。
在方法addCircle
:
右边的圆上多加一个圆就可以了
if toRight {
self.bezierPath.addArc(withCenter: point2, radius: r,
startAngle: 0*angle, endAngle: 2*360*angle,
clockwise: true)
} else {
self.bezierPath.addArc(withCenter: point2, radius: r,
startAngle: 0*angle, endAngle: 360*angle,
clockwise: true)
}
并将fillRule
设置为evenOdd
:
shapeLayer.fillColor = UIColor.blue.cgColor
shapeLayer.fillRule = .evenOdd
所以你会得到左边的圆圈将有一个绘图圆圈,并将由 evenOdd
规则填充。但是右边的圆圈会有两个画圈,会空出来。
evenOdd
规则(https://developer.apple.com/documentation/quartzcore/cashapelayerfillrule/1521843-evenodd):
Specifies the even-odd winding rule. Count the total number of path
crossings. If the number of crossings is even, the point is outside
the path. If the number of crossings is odd, the point is inside the
path and the region containing it should be filled.
我正在使用 UIBezierPath 创建以下形状,然后使用 CAShapeLayer 绘制它,如下所示。
然后我可以将 CAShapeLayer fillColor 属性 从 shapeLayer.fillColor = UIColor.clear.cgColor
更改为 shapeLayer.fillColor = UIColor.blue.cgColor
并获得以下形状。
代码如下:
import UIKit
class ViewController: UIViewController {
var line = [CGPoint]()
let bezierPath: UIBezierPath = UIBezierPath()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
let point1 = CGPoint(x: 100, y: 100)
let point2 = CGPoint(x: 400, y: 100)
line.append(point1)
line.append(point2)
addCircle(toRight: false)
addLine()
addCircle(toRight: true)
let shapeLayer = CAShapeLayer()
shapeLayer.path = bezierPath.cgPath
shapeLayer.strokeColor = UIColor.blue.cgColor
shapeLayer.fillColor = UIColor.clear.cgColor
self.view.layer.addSublayer(shapeLayer)
}
func addLine() -> Void {
bezierPath.move(to: line.first!)
bezierPath.addLine(to: line.last!)
}
func addCircle(toRight: Bool) -> Void {
let angle = CGFloat( Double.pi / 180 )
let r = CGFloat(20.0)
let x0 = toRight ? line.first!.x : line.last!.x
let y0 = toRight ? line.first!.y : line.last!.y
let x1 = toRight ? line.last!.x : line.first!.x
let y1 = toRight ? line.last!.y : line.first!.y
let x = x1 - x0
let y = y1 - y0
let h = (x*x + y*y).squareRoot()
let x2 = x0 + (x * (h + r) / h)
let y2 = y0 + (y * (h + r) / h)
// Add the arc, starting at that same point
let point2 = CGPoint(x: x2, y: y2)
let pointZeroDeg = CGPoint(x: x2 + r, y: y2)
self.bezierPath.move(to: pointZeroDeg)
self.bezierPath.addArc(withCenter: point2, radius: r,
startAngle: 0*angle, endAngle: 360*angle,
clockwise: true)
self.bezierPath.close()
}
}
但我其实想要的是下面的形状(左边的圆是填充的,右边的圆是空的)。
所以我的问题是,如何在 CAShapeLayer 上只填充形状的一部分? 可能吗?有什么技巧可以实现吗?
PS: 我可以通过创建 3 个不同的 UIBezierPaths(用于 leftCircle、line 和 rightCircle)并使用 3 个不同的 CAShapeLayers 绘制它们来实现这一点,如下所示。
// left Circle
shapeLayer1.path = bezierPath1.cgPath
shapeLayer1.fillColor = UIColor.blue.cgColor
// line
shapeLayer2.path = bezierPath2.cgPath
// right Circle
shapeLayer3.path = bezierPath3.cgPath
shapeLayer3.fillColor = UIColor.clear.cgColor
self.view.layer.addSublayer(shapeLayer1)
self.view.layer.addSublayer(shapeLayer2)
self.view.layer.addSublayer(shapeLayer3)
但我更喜欢使用单个 CAShapeLayer 来实现这一点。
我认为更好的方法是使用多种形状。
然而,您可以使用 evenOdd
形状填充规则来实现单一形状。
在方法addCircle
:
if toRight {
self.bezierPath.addArc(withCenter: point2, radius: r,
startAngle: 0*angle, endAngle: 2*360*angle,
clockwise: true)
} else {
self.bezierPath.addArc(withCenter: point2, radius: r,
startAngle: 0*angle, endAngle: 360*angle,
clockwise: true)
}
并将fillRule
设置为evenOdd
:
shapeLayer.fillColor = UIColor.blue.cgColor
shapeLayer.fillRule = .evenOdd
所以你会得到左边的圆圈将有一个绘图圆圈,并将由 evenOdd
规则填充。但是右边的圆圈会有两个画圈,会空出来。
evenOdd
规则(https://developer.apple.com/documentation/quartzcore/cashapelayerfillrule/1521843-evenodd):
Specifies the even-odd winding rule. Count the total number of path crossings. If the number of crossings is even, the point is outside the path. If the number of crossings is odd, the point is inside the path and the region containing it should be filled.