ios - 用图案图像填充多个 UIBezierPath

标签 ios swift uiview drawing uibezierpath

我创建了一个绘制大量 UIBezierPaths 的 UIView。现在我想用绿色纹理图像填充它们。使用以下代码,图案图像将在整个 View 中填充并重复,而不是在每个路径中。 (一条路径是底部带有圆圈的矩形之一)。所以我希望图案 Image 适合每条路径,并在路径旋转时旋转它。

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
    }

最佳答案

在某些情况下,您正在寻找的任务可能会变得复杂。但也许这样的事情就足够了:

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

}

此代码的作用是获取图像并将其绘制在具有给定旋转的所有路径上。

为此,它使用 addClip 剪辑上下文和路径,这意味着后面的每个绘制命令都只会在剪辑区域内绘制。

接下来绘制图像,需要确定其位置。我们尝试在应该绘制图像的地方拟合一个最小矩形。旋转路径以确定正确的边界非常重要,否则当旋转(例如)45 度时它们可能会太小。 (可选)将宽高比保留为“填充”模式,这需要一些数学知识......

然后旋转上下文,使图像看起来旋转。然后在给定路径上绘制图像。

剩下的只是清理,这意味着恢复状态。这将清除剪切蒙版和上下文旋转。

如果对保留方面填充的数学感兴趣: 我们正在尝试“填充”矩形内的大小。尺寸来自图像,而矩形是我们要绘制它的地方。我们需要比较两个比率(宽度/高度),并根据哪个较大,我们增加宽度或高度以绘制“越界”。在每种情况下,一个坐标都会被保留,而另一个坐标则由图像长宽比确定。这是确定“填充”矩形的通用解决方案。有趣的是,如果您需要“适合”,您所做的就是将不等式更改为 ifminimumBounds.width/minimumBounds.height>imageRatio{

关于ios - 用图案图像填充多个 UIBezierPath,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59731311/

相关文章:

IOS7 UIPickerView如何隐藏选择指示符

ios - 台风 Storyboard问题

swift - swift教程中的 'condition'是什么意思----函数?

ios - 使用参数创建自定义 UIView init 方法

ios - 如何在 xcode 8 中的 watch 和 iphone 之间共享数据

string - 如何快速删除字符串中的减号

iPhone 动画 : How to animate only subview ? 不是整个 View ?

ios - 通过标签动态查找 iOS UIView

ios - Xcode 7 不显示使用 Git 的新远程分支

ios - Gstreamer 不会一次播放多个流