I have a rectangle that I'm trying to do a reverse mask on, removing a variable number of other rectangles from the original shape.
If I were just trying to remove one rectangle, this would be easy, using an evenOdd fill. However, once I have multiple rectangles, the intersections of those rectangles become filled again. See the below example:
In the above example, I ideally, I wouldn't see the small red squares, which are the intersections of the 3 shapes overlayed onto the red background. In other words, the ideal is the inverse of this:
Is there any way I can eliminate the extra crossings in my Path so that the entire area "covered" by my Shape will get reverse-masked out?
Reproducible code to get to the first image:
struct MyShape: Shape {
func path(in rect: CGRect) -> Path {
var p = Path()
p.addRect(rect)
p.addRect(.init(origin: .init(x: 60, y: 60),
size: .init(width: 100, height: 100)))
p.addRect(.init(origin: .init(x: 120, y: 120),
size: .init(width: 100, height: 100)))
p.addRect(.init(origin: .zero,
size: .init(width: 100, height: 100)))
return p
}
}
struct ContentView: View {
var body: some View {
ZStack {
Color.red
.mask(
MyShape()
.fill(style: .init(eoFill: true))
)
}
.frame(width: 400, height: 300)
}
}
Note: I'm on macOS, but have a deployment target of 11.3, so a solution using CGPath's intersection won't work.
Also worth noting that in my basic example, I could hard code the path by hand, but assume that the overlayed rectangles are coming in dynamically, so hardcoding a path also isn't workable.


Rather than use your shape directly as a mask, you can create a "reverse mask" using
.blendMode(.destinationOut)to "cut out" a shape from aRectangle, then the result is used as the mask:A full (and excellent) explanation as to how this all works can be found here:
https://www.fivestars.blog/articles/reverse-masks-how-to/