Swift Combine下UIBezierPath合并后反转实现子路径镂空的问题问询
解决UIBezierPath多子路径镂空问题的两种可行方案
你遇到的问题本质是UIBezierPath.append(_:)仅做子路径拼接,不会处理路径间的重叠区域,直接对拼接后的路径调用reversing()依然是每个子路径单独反转,相交区域的缠绕计数会不符合镂空预期。以下是可落地的解决方案:
方案1:先对圆形路径做并集运算,再反转镂空
先把所有圆形路径合并为无内部重叠的单一路径,再整体反转追加到矩形路径中,即可实现你要的效果。
- iOS 16+/macOS 13+可直接使用系统自带的路径布尔运算API,示例代码如下:
import UIKit let circlePaths: [UIBezierPath] = ... let rectanglePath: UIBezierPath = ... // 你的基础矩形路径 // 第一步:所有圆形路径执行并集合并,得到无内部重叠的单一路径 var mergedCirclePath: UIBezierPath = .init() for circle in circlePaths { mergedCirclePath = mergedCirclePath.union(circle) } // 第二步:合并后的圆形路径整体反转后追加到矩形路径 rectanglePath.append(mergedCirclePath.reversing()) // 渲染时用默认的非零填充规则即可生效 let shapeLayer = CAShapeLayer() shapeLayer.path = rectanglePath.cgPath shapeLayer.fillRule = .nonZero
- 若要支持更低系统版本,可以引入开源路径运算库
ClippingBezier,调用对应并集方法合并路径即可,逻辑和上述代码一致。
方案2:用奇偶填充规则直接实现镂空(兼容性更好)
如果不想处理路径布尔运算和反转逻辑,直接修改填充规则即可实现需求,无需调整路径拼接逻辑:
let circlePaths: [UIBezierPath] = ... let rectanglePath: UIBezierPath = ... let totalPath: UIBezierPath = .init() for path in circlePaths { totalPath.append(path) } rectanglePath.append(totalPath) // 渲染时指定奇偶填充规则 let shapeLayer = CAShapeLayer() shapeLayer.path = rectanglePath.cgPath shapeLayer.fillRule = .evenOdd
该规则下,路径重叠次数为偶数的区域会自动被镂空,正好符合你要的所有圆形区域(包括重叠区域)从矩形中挖空的需求。如果是用UIGraphics直接绘制,设置rectanglePath.usesEvenOddFillRule = true后再填充即可。
内容的提问来源于stack exchange,提问作者Janosch Hübner
相关产品推荐
相关产品推荐

