如何将一些矩形UibezierPath对象加入一个?

发布于 2025-02-04 02:22:48 字数 651 浏览 2 评论 0原文

我只是在代码中进行以下操作:

    let path = UIBezierPath(rect: blurView.bounds)
    path.usesEvenOddFillRule = true
    path.append(UIBezierPath(rect: CGRect(x: 100, y: 100, width: 100, height: 100)))
    path.append(UIBezierPath(rect: CGRect(x: 150, y: 150, width: 100, height: 100)))
    //here you can add more paths, but the number is not known
    let layer = CAShapeLayer()
    layer.path = path.cgPath
    layer.fillRule = .evenOdd
    blurView.layer.mask = layer

效果如下:

​但是我需要的只是将两个矩形的区域结合在一起,而不是排除永恒的区域。是否可以?

I simply do the following in code:

    let path = UIBezierPath(rect: blurView.bounds)
    path.usesEvenOddFillRule = true
    path.append(UIBezierPath(rect: CGRect(x: 100, y: 100, width: 100, height: 100)))
    path.append(UIBezierPath(rect: CGRect(x: 150, y: 150, width: 100, height: 100)))
    //here you can add more paths, but the number is not known
    let layer = CAShapeLayer()
    layer.path = path.cgPath
    layer.fillRule = .evenOdd
    blurView.layer.mask = layer

and the effect is following:

enter image description here

Two rectangles overlapping one another. But all I need is to combine area from both rectanges, not to exclude everlapping area. Is it possible?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

温折酒 2025-02-11 02:22:48

使用“偶数”填充规则非常适合在路径中“切一个孔”。但是,此代码:

// create a big rect
let path = UIBezierPath(rect: blurView.bounds)
// cut a hole in it
path.append(UIBezierPath(rect: CGRect(x: 100, y: 100, width: 100, height: 100)))
// cut a hole overlapping a hole?
path.append(UIBezierPath(rect: CGRect(x: 150, y: 150, width: 100, height: 100)))

如您所见,将是有问题的。

根据您要做的一切,您 可以使用库,例如​​,您可以通过布尔动作来操纵路径。

或者,您可以使用像这样的自定义calayer将多个路径倒入“切割蒙版”:

class BasicCutoutLayer: CALayer {
    
    var rects: [CGRect] = []
    
    func addRect(_ newRect: CGRect) {
        rects.append(newRect)
        setNeedsDisplay()
    }
    func reset() {
        rects = []
        setNeedsDisplay()
    }
    
    override func draw(in ctx: CGContext) {
        
        // fill entire layer with solid color
        ctx.setFillColor(UIColor.gray.cgColor)
        ctx.fill(self.bounds);

        rects.forEach { r in
            ctx.addPath(UIBezierPath(rect: r).cgPath)
        }

        // draw clear "cutouts"
        ctx.setFillColor(UIColor.clear.cgColor)
        ctx.setBlendMode(.sourceIn)
        ctx.drawPath(using: .fill)
        
    }
    
}

要显示在使用中,我们将使用此图像:

在标准uiimageView中,用Blur uivisualeffectView覆盖,然后使用basic -cutoutlayer类,带有两个重叠的rects作为Blur View lays layer Mask:

class BasicCutoutVC: UIViewController {
    
    let myBlurView = UIVisualEffectView()
    let myCutoutLayer = BasicCutoutLayer()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = .systemBlue

        let imgView = UIImageView()
        if let img = UIImage(named: "sampleBG") {
            imgView.image = img
        }
        
        [imgView, myBlurView].forEach { v in
            v.translatesAutoresizingMaskIntoConstraints = false
            view.addSubview(v)
        }
        
        let g = view.safeAreaLayoutGuide
        NSLayoutConstraint.activate([
            
            imgView.topAnchor.constraint(equalTo: g.topAnchor),
            imgView.leadingAnchor.constraint(equalTo: g.leadingAnchor),
            imgView.trailingAnchor.constraint(equalTo: g.trailingAnchor),
            imgView.bottomAnchor.constraint(equalTo: g.bottomAnchor),
            
            myBlurView.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
            myBlurView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
            myBlurView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
            myBlurView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
            
        ])

        myBlurView.effect = UIBlurEffect(style: .extraLight)
        
        // set mask for blur view
        myBlurView.layer.mask = myCutoutLayer
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
    
        // set mask layer frame
        myCutoutLayer.frame = myBlurView.bounds
        
        // add two overlapping rects
        
        let v: CGFloat = 160
        let c: CGPoint = CGPoint(x: myBlurView.bounds.midX, y: myBlurView.bounds.midY)
        var r: CGRect = CGRect(origin: c, size: CGSize(width: v, height: v))

        r.origin.x -= v * 0.75
        r.origin.y -= v * 0.75
        myCutoutLayer.addRect(r)

        r.origin.x += v * 0.5
        r.origin.y += v * 0.5
        myCutoutLayer.addRect(r)
    }

}

在之前 :施加面具,看起来像这样:

”在此处输入图像说明”

应用掩模后,我们得到:

“在此处输入图像描述”

如我们所见,“重叠”显示。

那是一个非常简单的基本示例。对于一个更高级的示例,请看一下:

struct MyPath {
    var lineWidth: CGFloat = 0
    var lineCap: CGLineCap = .butt
    var lineJoin: CGLineJoin = .bevel
    var isStroked: Bool = true
    var isFilled: Bool = true
    var pth: UIBezierPath = UIBezierPath()
}

class AdvancedCutoutLayer: CALayer {
    
    var myPaths: [MyPath] = []
    
    func addPath(_ newPath: MyPath) {
        myPaths.append(newPath)
        setNeedsDisplay()
    }
    func reset() {
        myPaths = []
        setNeedsDisplay()
    }
    
    override func draw(in ctx: CGContext) {
        
        // fill entire layer with solid color
        ctx.setFillColor(UIColor.gray.cgColor)
        ctx.fill(self.bounds);
        ctx.setBlendMode(.sourceIn)

        myPaths.forEach { thisPath in
            ctx.setStrokeColor(thisPath.isStroked ? UIColor.clear.cgColor : UIColor.black.cgColor)
            ctx.setFillColor(thisPath.isFilled ? UIColor.clear.cgColor : UIColor.black.cgColor)
            ctx.setLineWidth(thisPath.isStroked ? thisPath.lineWidth : 0.0)
            ctx.setLineCap(thisPath.lineCap)
            ctx.setLineJoin(thisPath.lineJoin)
            ctx.addPath(thisPath.pth.cgPath)
            ctx.drawPath(using: .fillStroke)
        }
        
    }
    
}

以及一个子分类uivisualeffectView为了方便起见:

class CutoutBlurView: UIVisualEffectView {
    
    let sl = AdvancedCutoutLayer()
    
    override init(effect: UIVisualEffect?) {
        super.init(effect: effect)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() {
        sl.isOpaque = false
        layer.mask = sl
    }
    override func layoutSubviews() {
        super.layoutSubviews()
        sl.frame = bounds
        sl.setNeedsDisplay()
    }
    func addPath(_ newPath: MyPath) {
        sl.addPath(newPath)
    }
    func reset() {
        sl.reset()
    }
}

和一个示例控制器:

class AdvancedCutoutVC: UIViewController {
    
    let myView = CutoutBlurView()
    
    var idx: Int = 0
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = .systemBlue
        
        let imgView = UIImageView()
        if let img = UIImage(named: "sampleBG") {
            imgView.image = img
        }
        
        [imgView, myView].forEach { v in
            v.translatesAutoresizingMaskIntoConstraints = false
            view.addSubview(v)
        }
        
        let g = view.safeAreaLayoutGuide
        NSLayoutConstraint.activate([
            
            imgView.topAnchor.constraint(equalTo: g.topAnchor),
            imgView.leadingAnchor.constraint(equalTo: g.leadingAnchor),
            imgView.trailingAnchor.constraint(equalTo: g.trailingAnchor),
            imgView.bottomAnchor.constraint(equalTo: g.bottomAnchor),
            
            myView.topAnchor.constraint(equalTo: g.topAnchor),
            myView.leadingAnchor.constraint(equalTo: g.leadingAnchor),
            myView.trailingAnchor.constraint(equalTo: g.trailingAnchor),
            myView.bottomAnchor.constraint(equalTo: g.bottomAnchor),

        ])
        
        myView.effect = UIBlurEffect(style: .extraLight)
        
    }
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        Timer.scheduledTimer(withTimeInterval: 2.0, repeats: true, block: { _ in
            switch self.idx % 4 {
            case 1:
                self.addSomeOvals()
            case 2:
                self.addSomeLines()
            case 3:
                self.addSomeShapes()
            default:
                self.addSomeRects()
            }
            self.idx += 1
        })
    }
    func addSomeRects() {
        myView.reset()
        let w: CGFloat = myView.frame.width / 4.0
        let h: CGFloat = myView.frame.height / 4.0
        var x: CGFloat = ((myView.frame.width - (w * 5.0 * 0.5)) * 0.5) - (w * 0.25)
        var y: CGFloat = ((myView.frame.height - (h * 5.0 * 0.5)) * 0.5) - (h * 0.25)
        for _ in 1...5 {
            let bz = UIBezierPath(rect: CGRect(x: x, y: y, width: w, height: h))
            myView.addPath(MyPath(lineWidth: 0, isStroked: false, isFilled: true, pth: bz))
            x += w * 0.5
            y += h * 0.5
        }
    }
    func addSomeOvals() {
        myView.reset()
        let w: CGFloat = myView.frame.width / 4.0
        let h: CGFloat = myView.frame.height / 4.0
        var x: CGFloat = ((myView.frame.width - (w * 5.0 * 0.5)) * 0.5) - (w * 0.25)
        var y: CGFloat = ((myView.frame.height - (h * 5.0 * 0.5)) * 0.5) - (h * 0.25)
        for _ in 1...5 {
            let bz = UIBezierPath(ovalIn: CGRect(x: x, y: y, width: w, height: h))
            myView.addPath(MyPath(lineWidth: 0, isStroked: false, isFilled: true, pth: bz))
            x += w * 0.5
            y += h * 0.5
        }
    }
    func addSomeLines() {
        myView.reset()
        let w: CGFloat = myView.frame.width / 2.0
        let h: CGFloat = myView.frame.height / 4.0
        let x: CGFloat = 80
        var y: CGFloat = 80
        var lw: CGFloat = 4
        for _ in 1...5 {
            let bz = UIBezierPath()
            bz.move(to: CGPoint(x: x, y: y))
            bz.addLine(to: CGPoint(x: x + w, y: y + 20))
            myView.addPath(MyPath(lineWidth: lw, lineCap: .round, isStroked: true, isFilled: false, pth: bz))
            y += h * 0.5
            lw += 10
        }
    }
    func addSomeShapes() {
        myView.reset()
        var bz: UIBezierPath!
        
        bz = UIBezierPath(rect: CGRect(x: 80, y: 80, width: 80, height: 120))
        myView.addPath(MyPath(isStroked: false, isFilled: true, pth: bz))

        bz = UIBezierPath(rect: CGRect(x: 120, y: 120, width: 120, height: 60))
        myView.addPath(MyPath(isStroked: false, isFilled: true, pth: bz))

        bz = UIBezierPath(rect: CGRect(x: 80, y: 220, width: 220, height: 60))
        myView.addPath(MyPath(lineWidth: 12, isStroked: true, isFilled: false, pth: bz))
        
        bz = UIBezierPath(ovalIn: CGRect(x: 100, y: 240, width: 220, height: 60))
        myView.addPath(MyPath(lineWidth: 12, isStroked: true, isFilled: false, pth: bz))

        var r: CGRect = CGRect(x: 40, y: 320, width: myView.frame.width - 80, height: 200)
        for _ in 1...4 {
            bz = UIBezierPath(rect: r)
            myView.addPath(MyPath(lineWidth: 8, isStroked: true, isFilled: false, pth: bz))
            r = r.insetBy(dx: 20, dy: 20)
        }
    }
}

运行时,此示例将循环到重叠的rect,重叠的椭圆形,一些不同的宽度行, ,还有一些各种形状(只是为了给出一个想法):

”在此处输入图像描述”

“在此处输入图像描述”

Using the "even-odd" fill rule is great for "cutting a hole" in a path. However, this code:

// create a big rect
let path = UIBezierPath(rect: blurView.bounds)
// cut a hole in it
path.append(UIBezierPath(rect: CGRect(x: 100, y: 100, width: 100, height: 100)))
// cut a hole overlapping a hole?
path.append(UIBezierPath(rect: CGRect(x: 150, y: 150, width: 100, height: 100)))

will be, as you've seen, problematic.

Depending on what all you are wanting to do, you could use a library such as ClippingBezier which allows you to manipulate paths with boolean actions.

Or, you can use a custom CALayer like this to "invert" multiple paths to use as a "cutout mask":

class BasicCutoutLayer: CALayer {
    
    var rects: [CGRect] = []
    
    func addRect(_ newRect: CGRect) {
        rects.append(newRect)
        setNeedsDisplay()
    }
    func reset() {
        rects = []
        setNeedsDisplay()
    }
    
    override func draw(in ctx: CGContext) {
        
        // fill entire layer with solid color
        ctx.setFillColor(UIColor.gray.cgColor)
        ctx.fill(self.bounds);

        rects.forEach { r in
            ctx.addPath(UIBezierPath(rect: r).cgPath)
        }

        // draw clear "cutouts"
        ctx.setFillColor(UIColor.clear.cgColor)
        ctx.setBlendMode(.sourceIn)
        ctx.drawPath(using: .fill)
        
    }
    
}

To show it in use, we'll use this image:

In a standard UIImageView, overlaid with a blur UIVisualEffectView, and then use the BasicCutoutLayer class with two overlapping rects as the blur view's layer mask:

class BasicCutoutVC: UIViewController {
    
    let myBlurView = UIVisualEffectView()
    let myCutoutLayer = BasicCutoutLayer()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = .systemBlue

        let imgView = UIImageView()
        if let img = UIImage(named: "sampleBG") {
            imgView.image = img
        }
        
        [imgView, myBlurView].forEach { v in
            v.translatesAutoresizingMaskIntoConstraints = false
            view.addSubview(v)
        }
        
        let g = view.safeAreaLayoutGuide
        NSLayoutConstraint.activate([
            
            imgView.topAnchor.constraint(equalTo: g.topAnchor),
            imgView.leadingAnchor.constraint(equalTo: g.leadingAnchor),
            imgView.trailingAnchor.constraint(equalTo: g.trailingAnchor),
            imgView.bottomAnchor.constraint(equalTo: g.bottomAnchor),
            
            myBlurView.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
            myBlurView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
            myBlurView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
            myBlurView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
            
        ])

        myBlurView.effect = UIBlurEffect(style: .extraLight)
        
        // set mask for blur view
        myBlurView.layer.mask = myCutoutLayer
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
    
        // set mask layer frame
        myCutoutLayer.frame = myBlurView.bounds
        
        // add two overlapping rects
        
        let v: CGFloat = 160
        let c: CGPoint = CGPoint(x: myBlurView.bounds.midX, y: myBlurView.bounds.midY)
        var r: CGRect = CGRect(origin: c, size: CGSize(width: v, height: v))

        r.origin.x -= v * 0.75
        r.origin.y -= v * 0.75
        myCutoutLayer.addRect(r)

        r.origin.x += v * 0.5
        r.origin.y += v * 0.5
        myCutoutLayer.addRect(r)
    }

}

Before applying the mask, it looks like this:

enter image description here

after applying the mask we get:

enter image description here

As we see, the "overlap" displays as we want.

That was a very simple, basic example. For a more advanced example, take a look at this:

struct MyPath {
    var lineWidth: CGFloat = 0
    var lineCap: CGLineCap = .butt
    var lineJoin: CGLineJoin = .bevel
    var isStroked: Bool = true
    var isFilled: Bool = true
    var pth: UIBezierPath = UIBezierPath()
}

class AdvancedCutoutLayer: CALayer {
    
    var myPaths: [MyPath] = []
    
    func addPath(_ newPath: MyPath) {
        myPaths.append(newPath)
        setNeedsDisplay()
    }
    func reset() {
        myPaths = []
        setNeedsDisplay()
    }
    
    override func draw(in ctx: CGContext) {
        
        // fill entire layer with solid color
        ctx.setFillColor(UIColor.gray.cgColor)
        ctx.fill(self.bounds);
        ctx.setBlendMode(.sourceIn)

        myPaths.forEach { thisPath in
            ctx.setStrokeColor(thisPath.isStroked ? UIColor.clear.cgColor : UIColor.black.cgColor)
            ctx.setFillColor(thisPath.isFilled ? UIColor.clear.cgColor : UIColor.black.cgColor)
            ctx.setLineWidth(thisPath.isStroked ? thisPath.lineWidth : 0.0)
            ctx.setLineCap(thisPath.lineCap)
            ctx.setLineJoin(thisPath.lineJoin)
            ctx.addPath(thisPath.pth.cgPath)
            ctx.drawPath(using: .fillStroke)
        }
        
    }
    
}

along with a subclassed UIVisualEffectView for convenience:

class CutoutBlurView: UIVisualEffectView {
    
    let sl = AdvancedCutoutLayer()
    
    override init(effect: UIVisualEffect?) {
        super.init(effect: effect)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() {
        sl.isOpaque = false
        layer.mask = sl
    }
    override func layoutSubviews() {
        super.layoutSubviews()
        sl.frame = bounds
        sl.setNeedsDisplay()
    }
    func addPath(_ newPath: MyPath) {
        sl.addPath(newPath)
    }
    func reset() {
        sl.reset()
    }
}

and an example controller:

class AdvancedCutoutVC: UIViewController {
    
    let myView = CutoutBlurView()
    
    var idx: Int = 0
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = .systemBlue
        
        let imgView = UIImageView()
        if let img = UIImage(named: "sampleBG") {
            imgView.image = img
        }
        
        [imgView, myView].forEach { v in
            v.translatesAutoresizingMaskIntoConstraints = false
            view.addSubview(v)
        }
        
        let g = view.safeAreaLayoutGuide
        NSLayoutConstraint.activate([
            
            imgView.topAnchor.constraint(equalTo: g.topAnchor),
            imgView.leadingAnchor.constraint(equalTo: g.leadingAnchor),
            imgView.trailingAnchor.constraint(equalTo: g.trailingAnchor),
            imgView.bottomAnchor.constraint(equalTo: g.bottomAnchor),
            
            myView.topAnchor.constraint(equalTo: g.topAnchor),
            myView.leadingAnchor.constraint(equalTo: g.leadingAnchor),
            myView.trailingAnchor.constraint(equalTo: g.trailingAnchor),
            myView.bottomAnchor.constraint(equalTo: g.bottomAnchor),

        ])
        
        myView.effect = UIBlurEffect(style: .extraLight)
        
    }
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        Timer.scheduledTimer(withTimeInterval: 2.0, repeats: true, block: { _ in
            switch self.idx % 4 {
            case 1:
                self.addSomeOvals()
            case 2:
                self.addSomeLines()
            case 3:
                self.addSomeShapes()
            default:
                self.addSomeRects()
            }
            self.idx += 1
        })
    }
    func addSomeRects() {
        myView.reset()
        let w: CGFloat = myView.frame.width / 4.0
        let h: CGFloat = myView.frame.height / 4.0
        var x: CGFloat = ((myView.frame.width - (w * 5.0 * 0.5)) * 0.5) - (w * 0.25)
        var y: CGFloat = ((myView.frame.height - (h * 5.0 * 0.5)) * 0.5) - (h * 0.25)
        for _ in 1...5 {
            let bz = UIBezierPath(rect: CGRect(x: x, y: y, width: w, height: h))
            myView.addPath(MyPath(lineWidth: 0, isStroked: false, isFilled: true, pth: bz))
            x += w * 0.5
            y += h * 0.5
        }
    }
    func addSomeOvals() {
        myView.reset()
        let w: CGFloat = myView.frame.width / 4.0
        let h: CGFloat = myView.frame.height / 4.0
        var x: CGFloat = ((myView.frame.width - (w * 5.0 * 0.5)) * 0.5) - (w * 0.25)
        var y: CGFloat = ((myView.frame.height - (h * 5.0 * 0.5)) * 0.5) - (h * 0.25)
        for _ in 1...5 {
            let bz = UIBezierPath(ovalIn: CGRect(x: x, y: y, width: w, height: h))
            myView.addPath(MyPath(lineWidth: 0, isStroked: false, isFilled: true, pth: bz))
            x += w * 0.5
            y += h * 0.5
        }
    }
    func addSomeLines() {
        myView.reset()
        let w: CGFloat = myView.frame.width / 2.0
        let h: CGFloat = myView.frame.height / 4.0
        let x: CGFloat = 80
        var y: CGFloat = 80
        var lw: CGFloat = 4
        for _ in 1...5 {
            let bz = UIBezierPath()
            bz.move(to: CGPoint(x: x, y: y))
            bz.addLine(to: CGPoint(x: x + w, y: y + 20))
            myView.addPath(MyPath(lineWidth: lw, lineCap: .round, isStroked: true, isFilled: false, pth: bz))
            y += h * 0.5
            lw += 10
        }
    }
    func addSomeShapes() {
        myView.reset()
        var bz: UIBezierPath!
        
        bz = UIBezierPath(rect: CGRect(x: 80, y: 80, width: 80, height: 120))
        myView.addPath(MyPath(isStroked: false, isFilled: true, pth: bz))

        bz = UIBezierPath(rect: CGRect(x: 120, y: 120, width: 120, height: 60))
        myView.addPath(MyPath(isStroked: false, isFilled: true, pth: bz))

        bz = UIBezierPath(rect: CGRect(x: 80, y: 220, width: 220, height: 60))
        myView.addPath(MyPath(lineWidth: 12, isStroked: true, isFilled: false, pth: bz))
        
        bz = UIBezierPath(ovalIn: CGRect(x: 100, y: 240, width: 220, height: 60))
        myView.addPath(MyPath(lineWidth: 12, isStroked: true, isFilled: false, pth: bz))

        var r: CGRect = CGRect(x: 40, y: 320, width: myView.frame.width - 80, height: 200)
        for _ in 1...4 {
            bz = UIBezierPath(rect: r)
            myView.addPath(MyPath(lineWidth: 8, isStroked: true, isFilled: false, pth: bz))
            r = r.insetBy(dx: 20, dy: 20)
        }
    }
}

When run, this example will cycle through overlapping rect, overlapping ovals, some varying width lines, and some assorted shapes (just to give an idea):

enter image description hereenter image description here

enter image description hereenter image description here

晨曦÷微暖 2025-02-11 02:22:48

我会选择 clippingbezier 因为它很快,易于使用且整洁。它将是这样的:

let rect1 = CGRect(x: 100, y: 100, width: 200, height: 200)
let rect2 = CGRect(x: 150, y: 200, width: 200, height: 200)
        
let path0 = UIBezierPath(rect: blurView.bounds)
let path1 = UIBezierPath(rect: rect1)
let path2 = UIBezierPath(rect: rect2)
        
let unionPathArray = path1.union(with: path2)
let unionPath = UIBezierPath()
        
if let array = unionPathArray {
            
    array.forEach(unionPath.append)
            
    path0.append(unionPath.reversing())
    let layerUnion = CAShapeLayer()
    layerUnion.path = path0.cgPath
            
    blurView.layer.mask = layerUnion
}
        

输出:

”在此处输入图像说明“

ed

似乎在使用uibezierpath时,这种方法似乎无法正常工作:)。为了克服这一点,这是我们可以构建自己的功能来做到这一点的方式:


extension UIBezierPath {
    
    convenience init(rectangleIn rect: CGRect, cornerRadius: CGFloat) {
        self.init()
        
        move(to: CGPoint(x: rect.minX, y: rect.minY + cornerRadius))
        addArc(withCenter: CGPoint(x: rect.minX + cornerRadius, y: rect.minY + cornerRadius), radius: cornerRadius, startAngle: .pi, endAngle: 3.0 * .pi / 2.0, clockwise: true)
        addLine(to: CGPoint(x: rect.maxX - cornerRadius, y: rect.minY))
        addArc(withCenter: CGPoint(x: rect.maxX - cornerRadius, y: rect.minY + cornerRadius), radius: cornerRadius, startAngle: 3.0 * .pi / 2.0, endAngle: 2 * .pi, clockwise: true)
        
        addLine(to: CGPoint(x: rect.maxX, y: rect.maxY - cornerRadius))
        addArc(withCenter: CGPoint(x: rect.maxX - cornerRadius, y: rect.maxY - cornerRadius), radius: cornerRadius, startAngle: 0.0, endAngle: .pi / 2.0, clockwise: true)
        addLine(to: CGPoint(x: rect.minX + cornerRadius, y: rect.maxY))
        addArc(withCenter: CGPoint(x: rect.minX + cornerRadius, y: rect.maxY - cornerRadius), radius: cornerRadius, startAngle: .pi / 2.0, endAngle: .pi, clockwise: true)
        //addLine(to: CGPoint(x: rect.minX, y: rect.minY + cornerRadius))
        
        close()
    }
}

我们还可以将上述解决方案扩展到多个路径。这是创建多个路径联合的一种方法:


extension UIBezierPath {
    
    class func getUnion(of paths: [UIBezierPath]) -> UIBezierPath {
        var result = UIBezierPath()
        paths.forEach { subPath in
            guard let union = result.union(with: subPath) else { return }
            let unionCombined = UIBezierPath()
            union.forEach(unionCombined.append)
            result = unionCombined
        }
        return result
    }
  
}

这是一个示例


    let rect1 = CGRect(x: 100, y: 100, width: 200, height: 180)
    let rect2 = CGRect(x: 150, y: 200, width: 200, height: 200)
    let rect3 = CGRect(x: 150, y: 500, width: 100, height: 100)
    let rect4 = CGRect(x: 150, y: 800, width: 300, height: 100)
        
    let pathBase = UIBezierPath(rect: blurView.bounds)
    let path1 = UIBezierPath(rectangleIn: rect1, cornerRadius: 20.0)
    let path2 = UIBezierPath(rect: rect2)
    let path3 = UIBezierPath(ovalIn: rect3)
    let path4 = UIBezierPath(ovalIn: rect4)
        
    let union = UIBezierPath.getUnion(of: [path1, path2, path3, path4])
    pathBase.append(union.reversing())
    let layerUnion = CAShapeLayer()
    layerUnion.path = pathBase.cgPath
        
    blurView.layer.mask = layerUnion
        
        

“在此处输入图像描述”

I would go with ClippingBezier because it is fast, easy to use and neat. It'll be something like this:

let rect1 = CGRect(x: 100, y: 100, width: 200, height: 200)
let rect2 = CGRect(x: 150, y: 200, width: 200, height: 200)
        
let path0 = UIBezierPath(rect: blurView.bounds)
let path1 = UIBezierPath(rect: rect1)
let path2 = UIBezierPath(rect: rect2)
        
let unionPathArray = path1.union(with: path2)
let unionPath = UIBezierPath()
        
if let array = unionPathArray {
            
    array.forEach(unionPath.append)
            
    path0.append(unionPath.reversing())
    let layerUnion = CAShapeLayer()
    layerUnion.path = path0.cgPath
            
    blurView.layer.mask = layerUnion
}
        

Output:

enter image description here

EDIT

It appears that this method doesn't work properly when using UIBezierPath(roundedRect:cornerRadius:). To overcome that, here is how we can construct our own func to do that:


extension UIBezierPath {
    
    convenience init(rectangleIn rect: CGRect, cornerRadius: CGFloat) {
        self.init()
        
        move(to: CGPoint(x: rect.minX, y: rect.minY + cornerRadius))
        addArc(withCenter: CGPoint(x: rect.minX + cornerRadius, y: rect.minY + cornerRadius), radius: cornerRadius, startAngle: .pi, endAngle: 3.0 * .pi / 2.0, clockwise: true)
        addLine(to: CGPoint(x: rect.maxX - cornerRadius, y: rect.minY))
        addArc(withCenter: CGPoint(x: rect.maxX - cornerRadius, y: rect.minY + cornerRadius), radius: cornerRadius, startAngle: 3.0 * .pi / 2.0, endAngle: 2 * .pi, clockwise: true)
        
        addLine(to: CGPoint(x: rect.maxX, y: rect.maxY - cornerRadius))
        addArc(withCenter: CGPoint(x: rect.maxX - cornerRadius, y: rect.maxY - cornerRadius), radius: cornerRadius, startAngle: 0.0, endAngle: .pi / 2.0, clockwise: true)
        addLine(to: CGPoint(x: rect.minX + cornerRadius, y: rect.maxY))
        addArc(withCenter: CGPoint(x: rect.minX + cornerRadius, y: rect.maxY - cornerRadius), radius: cornerRadius, startAngle: .pi / 2.0, endAngle: .pi, clockwise: true)
        //addLine(to: CGPoint(x: rect.minX, y: rect.minY + cornerRadius))
        
        close()
    }
}

We can also extend the above-mentioned solution to multiple paths. Here is one way to create the union of multiple paths:


extension UIBezierPath {
    
    class func getUnion(of paths: [UIBezierPath]) -> UIBezierPath {
        var result = UIBezierPath()
        paths.forEach { subPath in
            guard let union = result.union(with: subPath) else { return }
            let unionCombined = UIBezierPath()
            union.forEach(unionCombined.append)
            result = unionCombined
        }
        return result
    }
  
}

Here is an example:


    let rect1 = CGRect(x: 100, y: 100, width: 200, height: 180)
    let rect2 = CGRect(x: 150, y: 200, width: 200, height: 200)
    let rect3 = CGRect(x: 150, y: 500, width: 100, height: 100)
    let rect4 = CGRect(x: 150, y: 800, width: 300, height: 100)
        
    let pathBase = UIBezierPath(rect: blurView.bounds)
    let path1 = UIBezierPath(rectangleIn: rect1, cornerRadius: 20.0)
    let path2 = UIBezierPath(rect: rect2)
    let path3 = UIBezierPath(ovalIn: rect3)
    let path4 = UIBezierPath(ovalIn: rect4)
        
    let union = UIBezierPath.getUnion(of: [path1, path2, path3, path4])
    pathBase.append(union.reversing())
    let layerUnion = CAShapeLayer()
    layerUnion.path = pathBase.cgPath
        
    blurView.layer.mask = layerUnion
        
        

And the output:

enter image description here

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文