在UIView Swift中居中CAShapeLayer

时间:2018-08-14 17:40:59

标签: ios swift uiview cashapelayer

我无法在UIView中将CAShapeLayer居中。我已经搜索过,大多数解决方案来自2015年以前的Obj C版本,或者尚未解决。

图像的外观是附加的。当我检查它时,它在红色视图内,但是idk为什么它没有居中。我已经尝试过调整其大小,但仍然无法正常工作。

Image

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
}

2 个答案:

答案 0 :(得分:5)

正如@ukim所说,您的问题是您试图根据视图和它们的大小在有限之前确定它们的位置。

viewDidLoad中时,您还不知道视图的大小和最终位置。您可以添加progressView,但是直到viewDidLayoutSubviews(记录在here中)之前,不能确保其大小或位置正确。

因此,如果我将您对setupCircleLayers的呼叫移至viewDidLayoutSubviews,并且将中心点更改为CGPoint.zero,并将您的layer.position的计算结果更改为:

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

然后我看到了:

enter image description here

我希望这是您的目标。

这是完整的清单(请注意,由于我无权访问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()
    }
}

我已经更新了清单。

答案 1 :(得分:0)

您正在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