Rounded corner of a cut-out arc using UIBezierPath without off-setting the arc centre
03:55 15 Dec 2025

(This question is a sequel to this one—I'm trying to figure out how to adapt the code to new circumstances.)

I have a code that produces this shape:

A shape (in red) with rounded corners and cut-out arcs with separate rounded corners

The code is extracted into a function, producing CGPath:

private func makePath(
  width: CGFloat,
  height: CGFloat,
  cornerRadius: CGFloat,
  cutOutRadius: CGFloat,
  cutOutCornerRadius: CGFloat,
  cutOutPosition: CGFloat
) -> CGPath {
  let path = CGMutablePath()

  // Start at bottom-left before left cut-out.
  path.move(to: CGPoint(x: .zero, y: height - cornerRadius))

  // Up left edge to left cut-out (rounded corner).
  path.addArc(
    tangent1End: CGPoint(x: .zero, y: cutOutPosition + cutOutRadius),
    tangent2End: CGPoint(x: cutOutRadius, y: cutOutPosition + cutOutRadius),
    radius: cutOutCornerRadius
  )

  // Left cut-out arc (top to bottom, clockwise).
  path.addArc(
    center: CGPoint(x: cutOutCornerRadius, y: cutOutPosition),
    radius: cutOutRadius,
    startAngle: .pi / 2.0,
    endAngle: 3.0 * .pi / 2.0,
    clockwise: true
  )

  // Down left edge after cut-out to bottom-left (rounded corner).
  path.addArc(
    tangent1End: CGPoint(x: .zero, y: cutOutPosition - cutOutRadius),
    tangent2End: .zero,
    radius: cutOutCornerRadius
  )

  // Bottom-left corner to bottom-right corner.
  path.addArc(
    tangent1End: .zero,
    tangent2End: CGPoint(x: width, y: .zero),
    radius: cornerRadius
  )

  // Bottom-right corner up to right cut-out start (rounded corner).
  path.addArc(
    tangent1End: CGPoint(x: width, y: .zero),
    tangent2End: CGPoint(x: width, y: cutOutPosition - cutOutRadius),
    radius: cornerRadius
  )

  // Up right edge to right cutout (rounded corner).
  path.addArc(
    tangent1End: CGPoint(x: width, y: cutOutPosition - cutOutRadius),
    tangent2End: CGPoint(
      x: width - cutOutRadius, y: cutOutPosition - cutOutRadius
    ),
    radius: cutOutCornerRadius
  )

  // Right cut-out arc (bottom to top, counterclockwise).
  path.addArc(
    center: CGPoint(x: width - cutOutCornerRadius, y: cutOutPosition),
    radius: cutOutRadius,
    startAngle: 3.0 * .pi / 2.0,
    endAngle: .pi / 2.0,
    clockwise: true
  )

  // Up right edge after cut-out to top-right (rounded corner).
  path.addArc(
    tangent1End: CGPoint(x: width, y: cutOutPosition + cutOutRadius),
    tangent2End: CGPoint(x: width, y: height),
    radius: cutOutCornerRadius
  )

  // Top-right corner to top-left.
  path.addArc(
    tangent1End: CGPoint(x: width, y: height),
    tangent2End: CGPoint(x: .zero, y: height),
    radius: cornerRadius
  )

  // Top-left corner down to starting point.
  path.addArc(
    tangent1End: CGPoint(x: .zero, y: height),
    tangent2End: .zero,
    radius: cornerRadius
  )

  path.closeSubpath()

  return path
}

I need to tweak the code so that the centre of the cut-out arc is not moved horizontally towards the shape centre by the size of the radius, but stays on the shape edge. In other words, instead of this:

Path that adds rounded corners to a cut-out arc by shifting it

I need this:

Path that adds rounded corners to a cut-out arc without shifting it

I suppose it would involve even more complicated geometry, and I again fail to figure out how it could be implemented.

Any guidance is much appreciated!

P.S. Here's some code you could paste into Playgrounds along with the function above and have the complete working piece of software:

import PlaygroundSupport
import UIKit

let width = 300.0
let height = 200.0

let view = UIView(
  frame: CGRect(origin: .zero, size: CGSize(width: width, height: height))
)
view.backgroundColor = .clear

let shapeLayer = CAShapeLayer()
shapeLayer.frame = view.bounds
shapeLayer.fillColor = UIColor.systemRed.cgColor

shapeLayer.fillRule = .evenOdd
shapeLayer.path = makePath(
  width: width,
  height: height,
  cornerRadius: 20.0,
  cutOutRadius: 30.0,
  cutOutCornerRadius: 10.0,
  cutOutPosition: height * 0.6
)

view.layer.insertSublayer(shapeLayer, at: 0)

PlaygroundPage.current.liveView = view
ios swift uikit core-animation uibezierpath