You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将多个矩形UIBezierPath合并为单一路径并取区域并集?

问题原因

你当前的实现代码如下:

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

该实现使用的evenOdd(奇偶填充)规则判断逻辑为:从区域内任意点向外发射射线,若射线穿过路径边界的次数为奇数则填充该区域,为偶数则不填充。你直接将两个重叠矩形追加到外层大路径中时,两个矩形的重叠区域会被路径边界穿过2次(偶数),因此被判定为填充区域,也就是你看到的重叠部分没有被镂空。
你当前实现的效果如下:
效果示意图

这个需求完全可以实现,下面给出两种稳定可复用的方案,支持任意数量的镂空矩形,自动计算区域并集,不会扣除重叠部分。

方案1:图形上下文生成Mask(推荐,全版本兼容)

不需要处理复杂的路径填充规则和路径方向问题,直接通过绘制生成Mask图片,逻辑简单零出错,支持任意形状的镂空区域:

func setupBlurMask(for blurView: UIView, holeRects: [CGRect]) {
    let maskLayer = CALayer()
    maskLayer.frame = blurView.bounds
    UIGraphicsBeginImageContextWithOptions(blurView.bounds.size, false, 0)
    guard let context = UIGraphicsGetCurrentContext() else {
        UIGraphicsEndImageContext()
        blurView.layer.mask = maskLayer
        return
    }
    // 先填充整个区域为不透明黑色,对应毛玻璃正常显示的部分
    context.setFillColor(UIColor.black.cgColor)
    context.fill(blurView.bounds)
    // 将所有镂空矩形区域填充为透明,对应毛玻璃消失的镂空部分
    context.setBlendMode(.clear)
    context.setFillColor(UIColor.clear.cgColor)
    holeRects.forEach { context.fill($0) }
    
    let maskImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    maskLayer.contents = maskImage?.cgImage
    blurView.layer.mask = maskLayer
}

// 调用示例
let holeRects = [
    CGRect(x: 100, y: 100, width: 100, height: 100),
    CGRect(x: 150, y: 150, width: 100, height: 100)
    // 可追加任意数量的矩形,无需处理重叠问题
]
setupBlurMask(for: blurView, holeRects: holeRects)
方案2:路径合并实现(iOS16+可用)

如果不想使用图片生成Mask,可以先将所有镂空矩形合并为单个并集路径,反转路径方向后追加到外层大路径,使用nonZero填充规则即可:

let outerPath = UIBezierPath(rect: blurView.bounds)
var holePath = UIBezierPath()
let holeRects = [
    CGRect(x: 100, y: 100, width: 100, height: 100),
    CGRect(x: 150, y: 150, width: 100, height: 100)
    // 可追加任意数量矩形
]
holeRects.forEach { holePath.append(UIBezierPath(rect: $0)) }
// 合并所有镂空路径为并集
if let mergedPath = holePath.cgPath.union(holePath.cgPath) {
    holePath = UIBezierPath(cgPath: mergedPath)
}
// 反转镂空路径方向,追加到外层路径
outerPath.append(holePath.reversing())

let maskLayer = CAShapeLayer()
maskLayer.path = outerPath.cgPath
maskLayer.fillRule = .nonZero
blurView.layer.mask = maskLayer

注意:如果直接追加反转后的单个矩形路径而不做并集合并,重叠区域依然会因为缠绕值计算问题出现填充异常,因此必须先将所有镂空区域合并为单一路径再处理。

内容的提问来源于stack exchange,提问作者kunass2

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 09:06:52