I'm drawing a cut-out hole in CAShapeLayer using UIBezierPath, this is the code I have so far:
let view = UIView(
frame: CGRect(origin: .zero, size: CGSize(width: 500.0, height: 500.0))
)
view.backgroundColor = .clear
let shapeLayer = CAShapeLayer()
shapeLayer.frame = view.bounds
shapeLayer.fillColor = UIColor.systemMint.cgColor
let mainPath = UIBezierPath(
roundedRect: CGRect(
origin: CGPoint(x: 50.0, y: 50.0), size: CGSize(width: 400.0, height: 400.0)
),
cornerRadius: 10.0
)
let cutOutArc = UIBezierPath()
cutOutArc.addArc(
withCenter: CGPoint(x: 50.0, y: 250.0),
radius: 50.0,
startAngle: .pi / 2.0,
endAngle: .pi + .pi / 2.0,
clockwise: false
)
cutOutArc.addLine(to: CGPoint(x: 50.0, y: 200.0))
cutOutArc.close()
mainPath.append(cutOutArc)
shapeLayer.fillRule = .evenOdd
shapeLayer.path = mainPath.cgPath
view.layer.insertSublayer(shapeLayer, at: 0)
(The code is simplified and adapted to use in Playground.)
The code results in this shape (looks exactly as I need):

What I can't figure out how to do is adding a corner radius to the cut-out arc (where the red arrows in the image below point), say, of 4.0 points. I've played with pretty random options for a couple days now with no meaningful result. I tried to ask AI, and it couldn't figure out it either (it just kept on producing broken code really).

Does anyone know how to do it? Any guidance is much appreciated!
You can move the center of the arc a little bit to the right, by the corner radius that you want, so that the rounded corners "join up" with the semicircular arc.
That is, you are connecting up two little rounded 90-degree arcs with a big 180-degree arc. This picture shows the two little arcs,
and after having the 180-degree arc sit on top of them, it becomes
The little arcs (including the ones for the rounded rectangle) can be drawn by spamming addArc(tangent1End:tangent2End:radius:), using a similar technique as I described here.
The tangent end points look like this:
In the code I have marked the corresponding points (1-7) in comments,
func createPath(
width: CGFloat,
height: CGFloat,
cutoutRadius: CGFloat,
cornerRadius: CGFloat
) -> CGPath {
let center = CGPoint(x: cornerRadius, y: height / 2)
let path = CGMutablePath()
path.move(to: .init(x: 0, y: height - cornerRadius)) // 1
path.addArc(
tangent1End: .init(x: 0, y: height / 2 + cutoutRadius), // 2
tangent2End: .init(x: cutoutRadius, y: height / 2 + cutoutRadius), // 3
radius: cornerRadius
)
path.addArc(
center: center,
radius: cutoutRadius,
startAngle: .pi / 2,
endAngle: 3 * .pi / 2,
clockwise: true
)
// now path.currentPoint is at X
path.addArc(
tangent1End: .init(x: 0, y: height / 2 - cutoutRadius), // 4
tangent2End: .zero, // 5
radius: cornerRadius
)
path.addArc(
tangent1End: .zero, // 5
tangent2End: .init(x: width, y: 0), // 6
radius: cornerRadius
)
path.addArc(
tangent1End: .init(x: width, y: 0), // 6
tangent2End: .init(x: width, y: height), // 7
radius: cornerRadius
)
path.addArc(
tangent1End: .init(x: width, y: height), // 7
tangent2End: .init(x: 0, y: height), // 1
radius: cornerRadius
)
path.addArc(
tangent1End: .init(x: 0, y: height), // 1
tangent2End: .zero,
radius: cornerRadius
)
path.closeSubpath()
return path
}