在UIView Swift中居中CAShapeLayer

我很难将CAShapeLayer集中在UIView中。 我已经搜索过,大多数解决方案都是从2015年前的Obj C开始的,或者还没有解决。

附件是图像的样子。 当我检查它,它在红色视图内,但idk为什么它不居中。 我已经尝试过调整它但仍然无法正常工作。

图片

let progressView: UIView = { let view = UIView() view.backgroundColor = .red return view }() //MARK: - ViewDidLoad override func viewDidLoad() { super.viewDidLoad() view.addSubview(progressView) progressView.anchor(top: nil, left: nil, bottom: nil, right: nil, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 200, height: 200) progressView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true progressView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true setupCircleLayers() } var shapeLayer: CAShapeLayer! private func setupCircleLayers() { let trackLayer = createCircleShapeLayer(strokeColor: UIColor.rgb(red: 56, green: 25, blue: 49, alpha: 1), fillColor: #colorLiteral(red: 0.9686274529, green: 0.78039217, blue: 0.3450980484, alpha: 1)) progressView.layer.addSublayer(trackLayer) } private func createCircleShapeLayer(strokeColor: UIColor, fillColor: UIColor) -> CAShapeLayer { let centerpoint = CGPoint(x: progressView.frame.width / 2, y: progressView.frame.height / 2) let circularPath = UIBezierPath(arcCenter: centerpoint, radius: 100, startAngle: 0, endAngle: 2 * CGFloat.pi, clockwise: true) let layer = CAShapeLayer() layer.path = circularPath.cgPath layer.fillColor = fillColor.cgColor layer.lineCap = kCALineCapRound layer.position = progressView.center return layer } 

正如@ukim所说,你的问题在于你在确定图层的位置之前,根据视图及其大小确定图层的位置是有限的。

当您在viewDidLoad您还不知道视图的大小和最终位置。 您可以添加progressView但在viewDidLayoutSubviews ( 此处记录 )之前,您无法确定其大小或位置是否正确。

因此,如果我将您对setupCircleLayers的调用setupCircleLayers viewDidLayoutSubviews并将中心点更改为CGPoint.zero并将CGPoint.zero的计算更改为:

 layer.position = CGPoint(x: progressView.frame.size.width / 2, y: progressView.frame.size.height / 2) 

然后我看到了这个:

在此处输入图像描述

我希望更多的是你的目标。

这是完整的列表(注意我必须更改你的一些方法,因为我没有访问anchorUIColor.rgb ,但你可以解决这个问题:))

 import UIKit class ViewController: UIViewController { var shapeLayer: CAShapeLayer! let progressView: UIView = { let view = UIView() view.backgroundColor = .red return view }() override func viewDidLoad() { super.viewDidLoad() view.addSubview(progressView) progressView.translatesAutoresizingMaskIntoConstraints = false progressView.heightAnchor.constraint(equalToConstant: 200).isActive = true progressView.widthAnchor.constraint(equalToConstant: 200).isActive = true progressView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true progressView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true } override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() if shouldAddSublayer { setupCircleLayers() } } private func setupCircleLayers() { let trackLayer = createCircleShapeLayer(strokeColor: UIColor.init(red: 56/255, green: 25/255, blue: 49/255, alpha: 1), fillColor: #colorLiteral(red: 0.9686274529, green: 0.78039217, blue: 0.3450980484, alpha: 1)) progressView.layer.addSublayer(trackLayer) } private var shouldAddSublayer: Bool { /* check if: 1. we have any sublayers at all, if we don't then its safe to add a new, so return true 2. if there are sublayers, see if "our" layer is there, if it is not, return true */ guard let sublayers = progressView.layer.sublayers else { return true } return sublayers.filter({ $0.name == "myLayer"}).count == 0 } private func createCircleShapeLayer(strokeColor: UIColor, fillColor: UIColor) -> CAShapeLayer { let centerpoint = CGPoint.zero let circularPath = UIBezierPath(arcCenter: centerpoint, radius: 100, startAngle: 0, endAngle: 2 * CGFloat.pi, clockwise: true) let layer = CAShapeLayer() layer.path = circularPath.cgPath layer.fillColor = fillColor.cgColor layer.lineCap = kCALineCapRound layer.position = CGPoint(x: progressView.frame.size.width / 2, y: progressView.frame.size.height / 2) layer.name = "myLayer" return layer } } 

希望有所帮助。

警告

当您执行上述操作时,这也意味着每次调用viewDidLayoutSubviews ,您都要添加一个新图层。 为了避免这种情况,您可以使用图层的name属性

 layer.name = "myLayer" 

然后检查您是否已添加图层。 像这样的东西应该工作:

 private var shouldAddSublayer: Bool { /* check if: 1. we have any sublayers at all, if we don't then its safe to add a new, so return true 2. if there are sublayers, see if "our" layer is there, if it is not, return true */ guard let sublayers = progressView.layer.sublayers else { return true } return sublayers.filter({ $0.name == "myLayer"}).count == 0 } 

然后你在这里使用:

 override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() if shouldAddSublayer { setupCircleLayers() } } 

我已经更新了这个列表。

您正在viewDidLoad()中调用setupCircleLayers() viewDidLoad() 。 当时尚未根据约束计算progressView.frame

尝试

 let centerpoint = CGPoint(x: 100, y: 100) 

而不是从progressView.frame计算值

您可以在游乐场尝试:

 import UIKit import PlaygroundSupport class MyVC: UIViewController { let progressView: UIView = { let view = UIView() view.backgroundColor = .red return view }() var layer: CAShapeLayer? override func viewDidLoad() { super.viewDidLoad() view.addSubview(progressView) progressView.translatesAutoresizingMaskIntoConstraints = false progressView.backgroundColor = .red progressView.widthAnchor.constraint(equalToConstant: 200).isActive = true progressView.heightAnchor.constraint(equalToConstant: 200).isActive = true progressView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true progressView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true setupCircleLayers() } var shapeLayer: CAShapeLayer! private func setupCircleLayers() { let trackLayer = createCircleShapeLayer(strokeColor: .red, fillColor: #colorLiteral(red: 0.9686274529, green: 0.78039217, blue: 0.3450980484, alpha: 1)) progressView.layer.addSublayer(trackLayer) } private func createCircleShapeLayer(strokeColor: UIColor, fillColor: UIColor) -> CAShapeLayer { let centerpoint = CGPoint(x: 100, y: 100) let circularPath = UIBezierPath(arcCenter: centerpoint, radius: 100, startAngle: 0, endAngle: 2 * CGFloat.pi, clockwise: true) let layer = CAShapeLayer() layer.path = circularPath.cgPath layer.fillColor = fillColor.cgColor layer.lineCap = kCALineCapRound return layer } } let containerView = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 375.0, height: 667.0)) let vc = MyVC() PlaygroundPage.current.liveView = vc.view