Как заполнить только часть фигуры на CAShapeLayer - PullRequest
1 голос
/ 08 апреля 2020

Я создаю следующую фигуру с помощью UIBezierPath, а затем рисую ее с помощью CAShapeLayer следующим образом.

enter image description here

Затем я могу изменить свойство fillColor CAShapeLayer с shapeLayer.fillColor = UIColor.clear.cgColor до shapeLayer.fillColor = UIColor.blue.cgColor и получите следующую форму.

enter image description here

Вот код:

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

Но что я на самом деле хочу имеет следующую форму (левый круг заполнен, правый круг не заполнен).

enter image description here

Поэтому мой вопрос: Как заполнить только часть фигуры на 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.

1 Ответ

2 голосов
/ 08 апреля 2020

Я думаю, что лучший подход - это использование нескольких фигур.

Однако вы можете сделать реализацию одной формы, используя 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):

Указывает четное нечетное правило намотки. Подсчитайте общее количество пересечений путей. Если число пересечений четное, точка находится вне пути. Если число пересечений нечетное, точка находится внутри пути, и область, содержащая ее, должна быть заполнена.

...