Search code examples
iosswiftdrawingcore-animation

iOS: centering CAShapeLayer inside a constrained subview


I'm trying to render a shape in the center of Circle, a custom subclass of UIView (first snippet). Then I'm adding the Circle as a subview in my VC and adding constraints (second snippet).

When height and width is constrained, the rendered shape has its center (undesirably) in the top left corner of the Circle view (picture bellow). When only X and Y of the Circle view are constrained, the shape is in center as expected.

Why does this happen and what do I do to be able to constrain Circle as I please but still have the shape render in the center of Circle ? Thanks.

Circle:

class Circle: UIView {

func render(){
    let shapeLayer = CAShapeLayer()
    let center = self.center
    let endAngle = 2 * CGFloat.pi
    let circularPath = UIBezierPath(arcCenter: center, radius: 100, startAngle: 0, endAngle: endAngle, clockwise: true)

    shapeLayer.path = circularPath.cgPath
    self.layer.addSublayer(shapeLayer)
  }
}

VC:

class VC: UIViewController {
let circle = Circle()

override func viewDidLoad() {
    super.viewDidLoad()        
    configure()
    circle.render()
}

func configure(){
    view.backgroundColor = .white
    view.addSubview(progressCircle)
    progressCircle.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([
        progressCircle.centerYAnchor.constraint(equalTo: view.centerYAnchor),
        progressCircle.centerXAnchor.constraint(equalTo: view.centerXAnchor),
        progressCircle.heightAnchor.constraint(equalToConstant: 200),
        progressCircle.widthAnchor.constraint(equalToConstant: 200)
    ])
  }
}

Result:

result


Solution

  • Here is fixed view (tested with Xcode 11.4 / Playground)

    class Circle: UIView {
        let shapeLayer = CAShapeLayer()
    
    override func layoutSubviews() {
        super.layoutSubviews()
        shapeLayer.position = CGPoint(x: self.bounds.midX, y: self.bounds.midY)
    }
    
    func render(){
        let center = self.center
        let endAngle = 2 * CGFloat.pi
        let circularPath = UIBezierPath(arcCenter: center, radius: 100, startAngle: 0, endAngle: endAngle, clockwise: true)
    
        shapeLayer.path = circularPath.cgPath
        self.layer.addSublayer(shapeLayer)
      }
    }