skip to Main Content

I need to draw a rounded rect. However, at very special corner radius values (between 34 to 37), it causes very strange bugs like the image below. Can someone explain to me why it happened?

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        drawView()
        self.view.backgroundColor = .red
    }

    func drawView() {
        let rect = CGRect(x: 100, y: 100, width: 200, height: 100)
        let borderWidth = 10.0
        let cornerRadius = 34.0
        
        
        let layer = CAShapeLayer()
        layer.strokeColor = UIColor.green.cgColor
        layer.lineWidth = borderWidth
        layer.fillColor = UIColor.white.cgColor
        
        let path = UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius)
        
        
        layer.path = path.cgPath
        
        self.view.layer.addSublayer(layer)
        
    }
}


enter image description here

2

Answers


  1. Chosen as BEST ANSWER

    Using 2 bezierpath and fill with color instead of using single path with stroke.

    The problem is CALayer using multiple path to create a border, cause overlap between the path then it mixed with fill rule .evenOdd cause weird behavior. This also make impossible to create a perfect circle.

    This article explained why is it happened.

    https://www.paintcodeapp.com/blogpost/code-for-ios-7-rounded-rectangles

    Thanks Zalo iOS VietNam team to help me understand this.

    My solution to avoid overlap by using 2 separated paths and fill color between them.

    The another way to fix is using CGMutablePath.

     func draw() {
            let rect = CGRect(x: 30, y: 50, width: 200, height: 100)
            let boderWidth: CGFloat = 5
            let cornerRadius: CGFloat = 5
            
            
            
            let outterPath = UIBezierPath(roundedRect: rect, byRoundingCorners: .allCorners, cornerRadii: CGSize(width: cornerRadius, height: cornerRadius))
            
            let innerRect = CGRect(x: rect.minX + boderWidth, y: rect.minY + boderWidth, width: rect.width - boderWidth * 2, height: rect.height - boderWidth * 2)
            let innerPath = UIBezierPath(roundedRect: innerRect, byRoundingCorners: .allCorners, cornerRadii: CGSize(width: cornerRadius, height: cornerRadius))
            
            let path = UIBezierPath()
            path.append(outterPath)
            path.append(innerPath)
            
            let layer = CAShapeLayer()
            layer.fillColor = UIColor.red.cgColor
            layer.fillRule = .evenOdd
            layer.path = path.cgPath
            
            self.view.layer.addSublayer(layer)
        }
    

  2. I suspect that a rounded rectangle is implemented by joining some curves together and the artifact that you are seeing is caused by the lineJoin property of the layer.

    Setting lineJoin to .round fixes it for me:

    layer.lineJoin = .round
    

    enter image description here

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search