Search code examples
swiftuibezierpath

How to smooth curve in UIBezierPath symmetrically


I am writing a custom component in Swift with UIBezierPath and I am facing difficulties to correctly find the control points to create a perfect path around a circle. Please have a look of the component below:

enter image description here

Please note that the circle is not following the yellow ball perfectly in the blue circle area, I tried to set the control points to the right position but they are not resulting a good result:

enter image description here

The function used to describe the path above is partially this:


//private let MAGIC_NUMBER: CGFloat = 0.552284749831
private let MAGIC_NUMBER: CGFloat = 0.5

//...

let secondPointEnd: CGPoint = CGPoint(x: middleLeft.x + ballRadius/2, y: middleLeft.y + ballRadius/2)

path.addCurve(
    to: secondPointEnd,
    controlPoint1: CGPoint(x: firstPointEnd.x, y: secondPointEnd.y - (ballRadius * MAGIC_NUMBER)),
    controlPoint2: CGPoint(x: firstPointEnd.x, y: secondPointEnd.y))

let thirdPointStart: CGPoint = CGPoint(x: secondPointEnd.x + ballRadius/2, y: secondPointEnd.y - ballRadius/2)
path.addCurve(
    to: thirdPointStart,
    controlPoint1: CGPoint(x: thirdPointStart.x, y: secondPointEnd.y),
    controlPoint2: CGPoint(x: thirdPointStart.x, y: secondPointEnd.y - (ballRadius * MAGIC_NUMBER)))

For the full source code (Playground): https://gist.github.com/ppamorim/feb3318ac30e20a75d9c62e8abdc2efa

I tried to fix it with multiple configurations, I had no success so far, I also tried to apply the magic number 0.552284749831 but it makes it worse. Am I missing some control point configuration? Is there any invalid control point there?

Could you guys please help me to find what is wrong with this bezier path?

Regards,

Pedro

With help from @Bob, I was able to complete the UIBezierPath like he described: https://gist.github.com/ppamorim/9390708c455a950a65621715ce7b6c82


Solution

  • I wouldn’t recommend rendering the circular bit with beziers. They’re extremely close, but not spot on. It’s fine approximation for corner rounding of a rectangle (or any polygon), but for a true circular portion of the path you should use addArc(withCenter:radius:startAngle:endAngle:clockwise:).

    You can use beziers leading into and out of the arc, if you want, but for the circular portion, use an arc. E.g. see https://stackoverflow.com/a/60862388/1271826.