Как вычесть пересечение UIBezierPath A & B из A? - PullRequest
0 голосов
/ 15 декабря 2018

Предположим, у нас есть два UIBezierPaths, path1 и path2 ... (которые уже определены относительно границ представления во время выполнения и уже существуют как свойства того же представления).

Мы хотим получить новыйпуть типа UIBezierPath, path3, который является результатом вычитания path2 из path1:

enter image description here

Способ, которым это сделано (как видно здесь) должен сделать это:

path1.append(path2.reversing())

НО, который, кажется, работает только в тех случаях, когда путь1 полностью охватывает путь 2.

Например, рассмотрим случай, когда существует толькочастичное пересечение - путь1 не полностью охватывает путь2.Вот что произойдет, если мы применим тот же метод, что и выше:

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 голосов
/ 27 декабря 2018

Нет прямого способа получить различия UIBezierPath в качестве нового пути на 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

Вы можете использовать стороннюю библиотеку, например, от автора по имени Адам Вульф, здесь: 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()
    }
}

Если вы хотите использовать эту библиотеку, вы должны заметить небольшую подсказку: в Другие флаги компоновщика в настройках проекта, какописанный в readme, «-ObjC ++ -lstdc ++» должен быть добавлен, в противном случае он будет построен без нареканий, но не будет молча загружать фреймворки и, в конечном итоге, аварийно завершить работу, поскольку категории UIBEzierPath не найдены.

Результатвыглядит следующим образом:

real bezier path result

Таким образом, это действительно даст желаемый результат, но вам придется использовать стороннюю библиотеку.

0 голосов
/ 20 декабря 2018

Я достиг желаемого вами решения, но я создал код для статического просмотра размером 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Я надеюсь, что это поможет вам, дайте мне знать, если у вас есть какие-либо вопросы.

...