Заполните несколько UIBezierPath с рисунком - PullRequest
0 голосов
/ 14 января 2020

Я создал UIView, который др aws много UIBezierPaths. Теперь я хочу заполнить их зеленой текстурой Image. С помощью следующего кода шаблон изображения заполняется и повторяется во всем виде, а не в каждом отдельном пути. (One Path - один из этих прямоугольников с кружком внизу). Поэтому я хочу, чтобы шаблон изображения соответствовал каждому отдельному пути и поворачивал его при повороте пути.

result test

        var image =  UIImage(named: "test.jpg")!
    let color = UIColor.lightGray
    let fillColor = UIColor.init(patternImage: image)


    var lastPoint: CGPoint? = nil
    for point in points {
        if let lastPointUnwrapped = lastPoint{
            let path = BiberBezierPath(leftPoint: lastPointUnwrapped, rightPoint: point, frame: frame, plusHeight: -140)

            fillColor.setFill()


            path.fill()
            color.setStroke()
            path.lineWidth = 1
            path.lineCapStyle = .butt
            path.stroke()



        }
        lastPoint = point
    }

1 Ответ

1 голос
/ 14 января 2020

Задача, которую вы ищете, может быть сложной в некоторых случаях. Но может быть, что-то вроде этого должно быть достаточно:

class RoofView: UIView {

    var paths: [(path: UIBezierPath, rotationInRadians: CGFloat)]? { didSet { self.refresh() } }
    var image: UIImage? { didSet { self.refresh() } }
    var preserveImageAspect: Bool = true { didSet { self.refresh() } }

    func refresh() {
        self.setNeedsDisplay() // This will trigger redraw
    }

    override func draw(_ rect: CGRect) {
        super.draw(rect)

        guard let paths = paths else { return }
        guard let image = image else { return }
        guard let context = UIGraphicsGetCurrentContext() else { return }

        paths.forEach { item in
            context.saveGState() // Save state. This includes transformations and clips

            item.path.addClip()

            let boundingRect: CGRect = {
                let rotatedPath = (item.path.copy() as! UIBezierPath) // Create a duplicate path
                rotatedPath.apply(CGAffineTransform(rotationAngle: -item.rotationInRadians)) // Rotate it backwards

                if preserveImageAspect {
                    let minimumBounds = rotatedPath.bounds
                    let center = CGPoint(x: minimumBounds.midX, y: minimumBounds.midY)
                    let imageRatio: CGFloat = image.size.width / image.size.height
                    if minimumBounds.width / minimumBounds.height < imageRatio {
                        // Preserve height, scale width
                        let size = CGSize(width: minimumBounds.height*imageRatio, height: minimumBounds.height)
                        return CGRect(x: center.x - size.width*0.5, y: center.y - size.height*0.5, width: size.width, height: size.height)
                    } else {
                        // Preserve width, scale height
                        let size = CGSize(width: minimumBounds.width, height: minimumBounds.width/imageRatio)
                        return CGRect(x: center.x - size.width*0.5, y: center.y - size.height*0.5, width: size.width, height: size.height)
                    }
                } else {
                    return rotatedPath.bounds
                }
            }()

            context.rotate(by: item.rotationInRadians)
            image.draw(in: boundingRect)

            context.restoreGState() // Put back state. This includes transformations and clips
        }
    }

}

То, что этот код делает, берет изображение и др aws его на всех путях с заданными поворотами.

Для этого он обрезает контекст с помощью пути, используя addClip, что означает, что каждая последующая команда рисования будет нарисована только внутри обрезанной области.

Затем рисуется изображение, для которого необходимо определить позицию. Мы стараемся вписать минимальный прямоугольник, где должно быть нарисовано изображение. Важно повернуть путь, чтобы определить правильные границы, или они могут быть слишком маленькими при повороте (например, на 45 градусов). При желании соотношение сторон сохраняется как режим «заполнения», который требует немного математики ...

Затем контекст поворачивается так, что изображение кажется повернутым. Затем изображение рисуется по заданному пути.

Осталось только очистить, что означает восстановление состояния. Это очистит как обтравочную маску, так и вращение контекста.

Если вы заинтересованы в математике для сохранения заливки аспекта: Мы пытаемся «заполнить» размер в прямоугольнике. Размер берется из изображения, а прямоугольник - это то место, где мы хотим его нарисовать. Нам нужно сравнить как rat ios (width/height), так и в зависимости от того, что больше, мы либо увеличиваем ширину или высоту, чтобы нарисовать "вне границ". В каждом из случаев одна из координат сохраняется, а другая определяется с соотношением сторон изображения. Это общее решение для определения прямоугольника заполнения. Как забавный факт, если вам когда-либо понадобится «соответствовать», все, что вам нужно сделать, это изменить неравенство на if minimumBounds.width / minimumBounds.height > imageRatio {.

...