简体   繁体   中英

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:

结果

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)
  }
}

Ended up with this solution bellow (different shape this time but the principle applies). Work as expected, the view can now be constrained and moved in the VC however wanted.

class Circle: UIView {

private var layer: CAShapeLayer!
override func draw(_ rect: CGRect) {

    let width =  rect.width
    let height = rect.height        
    let lineWidth = 0.1 * min(width, height)
    let center = CGPoint(x: width / 2, y: height / 2)
    let radius = (min(width, height) - lineWidth) / 2
    let startAngle = -CGFloat.pi / 2
    let endAngle = startAngle + 2 * CGFloat.pi
    let circularPath = UIBezierPath(arcCenter: center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: true)

    layer = CAShapeLayer()
    layer.path = circularPath.cgPath
    layer.strokeColor = UIColor.lightGray.cgColor
    layer.lineWidth = lineWidth
    layer.fillColor = UIColor.clear.cgColor
    layer.lineCap = .round

    layer.addSublayer(backbroundLayer)

  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM