I'm trying to create an oval shape / rounded corners rect with UIBezierPath. What i want to achieve is this shape
One of the issues is that i wanst able to find the correct radius to archive my target shape and the second issue i have is that i can see lines sticking out, the code doesn't produce a clean shape
override func layoutSubviews() {
super.layoutSubviews()
layer.sublayers?.forEach { $0.removeFromSuperlayer() }
let path = makePath()
path.lineJoinStyle = .round
path.lineCapStyle = .round
let shapeLayer = CAShapeLayer()
// Enable antialiasing
shapeLayer.shouldRasterize = true
shapeLayer.rasterizationScale = UIScreen.main.scale
shapeLayer.lineJoin = .round
shapeLayer.path = path.cgPath
//shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.strokeColor = strokeColor.cgColor
shapeLayer.lineWidth = lineWidth
shapeLayer.lineCap = .round
layer.addSublayer(shapeLayer)
layer.backgroundColor = overlayColor.cgColor
//backgroundPath is the blur overlay
let backgroundPath = UIBezierPath(rect: bounds)
backgroundPath.lineJoinStyle = .round
backgroundPath.lineCapStyle = .round
let maskLayer = CAShapeLayer()
// Enable antialiasing
maskLayer.shouldRasterize = true
maskLayer.rasterizationScale = UIScreen.main.scale
maskLayer.frame = bounds
maskLayer.lineJoin = .round
//backgroundPath.append(path)
maskLayer.fillRule = .evenOdd
maskLayer.path = backgroundPath.cgPath
layer.mask = maskLayer
addAdditionalLayersIfNeeded(rect)
}
override func makePath(rect: CGRect) -> UIBezierPath {
UIBezierPath(roundedRect: preferedSize, byRoundingCorners: .allCorners, cornerRadii: CGSize(width: preferedSize.width * 0.33, height: preferedSize.height * 0.33))
}
dimensions that i'm using to create the shape
preferedSize: CGRect(x: 32, y: 278, width: 320, height: 400)
This is what the code will render


You've run into a couple bugs with
UIBezierPath(roundedRect: ...First, the
heightvalue ofcornerRadiiis ignored. So, let's ignore that as well and use:Basic
CAShapeLayerwith:.lineWidth = 20.0.strokeColor = UIColor.orange.cgColor.fillColor = UIColor.cyan.cgColorSo, we start with a corner radius of 25% and we'll increment it as we go:
As you see, when we hit corner radius of
29.5%, the actual radius jumps and we get a weird "gap" -- which also leaves a small misalignment, which is the "bump" you see on the sides.As we keep incrementing the percentage-of-width radius value, the actual radius remains constant until we get to
36.5%-- at which point the radius changes and the misalignment goes away (we get a smooth edge). Although, as we notice, the actual radius doesn't change after 36.5%Note that this will vary based on the actual size of the path and the width of the stroke.
If we do this:
print(path.cgPath(at 25%) we get this in the debug console:As we see, the "rounded rect" path is actually a series of line-to and curve-to instructions.
My guess is that Apple's internal
roundedRectalgorithm is hitting a floating-point error.One way to avoid the bugs is to use this extension to build the path ourselves:
Now, a 33% of width corner radius path generated like this:
gives us this:
Worth noting: Apple's algorithm generates a "continuous curve" rounded corner, which is slightly different.
This extension:
Gives this result:
Please Note: those extensions are slightly modified versions from the discussion here UIBezierPath bezierPathWithRoundedRect: the cornerRadius value is not consistent
Edit - ugh... I made the screen caps with some values typos...
Here is complete code to play around and compare:
// UIBezierPath extension
// Corner Type enum
// Custom View class
// Example View Controller
Looks like this when running: