Search code examples
swiftcalayeruibezierpathcashapelayer

Why is the drawn UIBezierPath positioned in the bottom right without specifying a central position in CAShapeLayer?


I tried to centralize UIBezierPath in a superview, but the UIBezierPath is positioned at the bottom right, not at the center.

I set the value of anchorPoint to (0.5, 0.5), position self.view.center or self.view.frame.width / 2, but the UIBezierPath is not centered.

import UIKit

/* ViewSection */
class ViewController: UIViewController {
    @IBOutlet weak var Toolbar: UIToolbar!
    @IBOutlet weak var UIToolbarButtonItem: UIBarButtonItem!
    @IBAction func UIToolbarButtonItem(_ sender: Any) {
        let objectFramePath = UIBezierPath.init(roundedRect: CGRect(x: 0, y:  0, width: 150, height: 150), byRoundingCorners: .allCorners, cornerRadii: CGSize(width: 39.5, height: 39.5))

        let shapeLayer = CAShapeLayer()
        shapeLayer.anchorPoint = CGPoint(x: 0.5, y: 0.5) 
        shapeLayer.position = self.view.center
        shapeLayer.fillColor = UIColor.blue.cgColor
        shapeLayer.path = objectFramePath.cgPath

        self.view.layer.addSublayer(shapeLayer)
    }


    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }
}

The result is this:

screenshot of the result


Solution

  • The anchor doesn’t work because the new layer doesn’t have a frame. If you set that, it works:

    let rect = CGRect(x: 0, y:  0, width: 150, height: 150)
    let path = UIBezierPath(roundedRect: rect, byRoundingCorners: .allCorners, cornerRadii: CGSize(width: 39.5, height: 39.5))
    
    let shapeLayer = CAShapeLayer()
    shapeLayer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
    shapeLayer.frame = rect
    shapeLayer.position = CGPoint(x: view.bounds.midX, y: view.bounds.midY)
    shapeLayer.fillColor = UIColor.blue.cgColor
    shapeLayer.path = path.cgPath
    
    view.layer.addSublayer(shapeLayer)
    

    Alternatively, you can just set the origin of the path:

    let size = CGSize(width: 150, height: 150)
    let origin = CGPoint(x: view.bounds.midX - size.width / 2,
                         y: view.bounds.midY - size.height / 2)
    
    let path = UIBezierPath(roundedRect: CGRect(origin: origin, size: size), byRoundingCorners: .allCorners, cornerRadii: CGSize(width: 39.5, height: 39.5))
    
    let shapeLayer = CAShapeLayer()
    shapeLayer.fillColor = UIColor.blue.cgColor
    shapeLayer.path = path.cgPath
    
    view.layer.addSublayer(shapeLayer)
    

    By the way, notice that I do not reference center, because as the docs say, “The center point is specified in points in the coordinate system of its superview.”

    Always use bounds (and if you need the center of that, the midX and midY of the bounds), because that will always be in the coordinate system of the view in question. In this case it just happens to not make much of a difference, but we should note that the middle of the bounds and the center within the superview are two completely different things.