This is adapted from @Asperi's answer here: https://stackoverflow.com/a/59659733/560942
struct MaskShape : Shape {
var inset : UIEdgeInsets
func path(in rect: CGRect) -> Path {
var shape = Rectangle().path(in: rect)
shape.addPath(Ellipse().path(in: rect.inset(by: inset)))
return shape
}
}
struct ContentView : View {
var body: some View {
ZStack {
Image("2")
.resizable()
.aspectRatio(contentMode: .fill)
GeometryReader { geometry in
Color.black.opacity(0.6)
.mask(
MaskShape(
inset: UIEdgeInsets(top: geometry.size.height / 6,
left: geometry.size.width / 6,
bottom: geometry.size.height / 6,
right: geometry.size.width / 6)
).fill(style: FillStyle(eoFill: true))
)
}
}
}
}

The ZStack
sets up the image and then a semi-transparent black overlay on the top. The overlay is cut away with a mask with eoFill
set to true
. I added some code to provide insets for the mask, as I'm assuming this might be a variably-sized mask.
Lots of minutiae that can be changed (like the image aspect ratio, the insets, etc), but it should get you started.