如何在 UITableViewCell 中居中自定义 CAShapeLayer?

How to center a custom CAShapeLayer inside UITableViewCell?

我使用 CAShapeLayer 创建了一个自定义的循环进度条。我可以使用 view.center 轻松地将它置于任何 UIView 的中心,但我需要将它置于 UITableView 单元格的中心,这就是我得到的:

如果您看一下该图像,上面的圆圈正确地显示了位于 UIView 中心的相同 CAShapeLayer

我试过了view.center, [x: view.frame.width /2, y :view.frame.height / 2 ] 我还尝试将 UIView 置于 Cell 的中心,并将 CAShapeLayer 添加到该居中的视图中……没用。

let center = CGPoint(x: self.frame.width/2, y: self.frame.height/2) 
let circularPath = UIBezierPath(arcCenter: .zero, radius: 80, startAngle: 0, endAngle: CGFloat.pi * 2, clockwise: true)
pulsatingLayer.position = center

完整代码:

import UIKit

class CurrentGACell: UITableViewCell {

    let shapeLayer = CAShapeLayer()

    var pulsatingLayer: CAShapeLayer!

    let percentageLabel: UILabel = {
        var label = UILabel()
        label.text = "Start"
        label.textAlignment = .center
        label.font = UIFont(name: "AvenirNext-DemiBold", size: 30)
        label.backgroundColor = UIColor.clear
        return label
    }()

    @IBOutlet weak var mainView: UIView!

    override func awakeFromNib() {
        super.awakeFromNib()
        setupNotificationObserver()

        let center = self.contentView.center //CGPoint(x: contentView.frame.width/2, y: contentView.frame.height/2) //mainView.center
        let circularPath = UIBezierPath(arcCenter: .zero, radius: 80, startAngle: 0, endAngle: CGFloat.pi * 2, clockwise: true)

        //pulsating
        pulsatingLayer = CAShapeLayer()
        pulsatingLayer.path = circularPath.cgPath
        pulsatingLayer.fillColor = UIColor.AppColors.Blue.withAlphaComponent(0.2).cgColor
        pulsatingLayer.strokeColor = UIColor.clear.cgColor
        pulsatingLayer.lineWidth = 10
        pulsatingLayer.lineCap = .round
        pulsatingLayer.position = center
        self.contentView.layer.addSublayer(pulsatingLayer)
        animatePulsatingLayer()

        //trackLayer
        let trackLayer = CAShapeLayer()
        trackLayer.path = circularPath.cgPath
        trackLayer.fillColor = UIColor.white.cgColor
        trackLayer.strokeColor = UIColor.AppColors.Blue.withAlphaComponent(0.2).cgColor
        trackLayer.lineWidth = 10
        trackLayer.lineCap = .round
        trackLayer.position = center
        self.contentView.layer.addSublayer(trackLayer)

        //shapeLayer
        shapeLayer.path = circularPath.cgPath
        shapeLayer.fillColor = UIColor.white.cgColor
        shapeLayer.strokeColor = UIColor.AppColors.Blue.cgColor
        shapeLayer.lineWidth = 10
        shapeLayer.strokeEnd = 0
        shapeLayer.lineCap = .round
        shapeLayer.position = center
        shapeLayer.transform = CATransform3DMakeRotation(-CGFloat.pi/2, 0, 0, 1)
        self.contentView.layer.addSublayer(shapeLayer)

        self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleTap)))

        self.contentView.addSubview(percentageLabel)
        percentageLabel.frame = CGRect(x: 0, y: 0, width: 500, height: 100)
        percentageLabel.center = center

    }

    @objc private func handleTap() {
        //beginDownloading()
        animateCircle()
    }

    fileprivate func animateCircle() {
        print("animating....")
        let basicAnimation = CABasicAnimation(keyPath: "strokeEnd")
        basicAnimation.toValue = 0.65
        basicAnimation.duration = 1
        basicAnimation.fillMode = CAMediaTimingFillMode.forwards
        basicAnimation.isRemovedOnCompletion = false

        percentageLabel.text = "29w + 3d"

        shapeLayer.add(basicAnimation, forKey: "urSoBasic")
    }

    private func animatePulsatingLayer() {
        let animation = CABasicAnimation(keyPath: "transform.scale")
        animation.toValue = 1.2
        animation.duration = 1
        animation.autoreverses = true
        animation.repeatCount = Float.infinity
        animation.timingFunction = CAMediaTimingFunction(name: .easeOut)
        pulsatingLayer.add(animation, forKey: "pulsating")
    }

    private func setupNotificationObserver() {
        NotificationCenter.default.addObserver(self, selector: #selector(handleEnterForegroung), name: UIApplication.willEnterForegroundNotification, object: nil)
    }
    @objc func handleEnterForegroung() {
        animatePulsatingLayer()
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    }

}

将依赖.contentView框架的代码移至layoutSubviews()。只有这样你的 CurrentGACell 才能保证有合适的框架。

类似于:

let shapeLayer = CAShapeLayer()
let trackLayer = CAShapeLayer()
var pulsatingLayer: CAShapeLayer!

func layoutSubviews() {
   super.layoutSubviews()
   let center = contentView.center
   pulsatingLayer.position = center
   trackLayer.position = center
   shapeLayer.position = center
   percentageLabel.center = center
}

您需要在 layoutSubviews 方法中添加所有代码。

let shapeLayer = CAShapeLayer()
override func layoutSubviews() {
    let circularPath = UIBezierPath(arcCenter: .zero, radius: 25, startAngle: 0, endAngle: CGFloat.pi * 2, clockwise: true)
    shapeLayer.path = circularPath.cgPath
    shapeLayer.strokeColor = UIColor.blue.cgColor
    shapeLayer.fillColor = UIColor.gray.cgColor
    shapeLayer.lineWidth = 1.0
    shapeLayer.position = self.contentView.center
    if !self.contentView.layer.sublayers!.contains(shapeLayer){
        self.layer.addSublayer(shapeLayer)
    }
}

您可以使用 UIScreen

的静态值
override func awakeFromNib() {
    super.awakeFromNib()
    let point = CGPoint.init(x: UIScreen.main.bounds.width/2, y: self.contentView.frame.height/2)
    let circularPath = UIBezierPath(arcCenter: .zero, radius: 25, startAngle: 0, endAngle: CGFloat.pi * 2, clockwise: true)
    let shapeLayer = CAShapeLayer()
    shapeLayer.path = circularPath.cgPath
    shapeLayer.strokeColor = UIColor.blue.cgColor
    shapeLayer.fillColor = UIColor.gray.cgColor
    shapeLayer.lineWidth = 1.0
    shapeLayer.position = point
    self.layer.addSublayer(shapeLayer)
}

注:

您得到了不可预测的结果,因为您在帧渲染之前添加了一个层。