Как центрировать пользовательский CAShapeLayer внутри UITableViewCell? - PullRequest
0 голосов
/ 14 июня 2019

Я создал пользовательский круговой индикатор выполнения, используя CAShapeLayer. Я могу легко центрировать его внутри любой UIView, используя view.center, но мне нужно центрировать его внутри UITableView ячейки, и вот что я получаю:

image

Если вы посмотрите на это изображение, над кружком будет показано то же CAShapeLayer, центрированное внутри UIView.

Я пробовал 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
    }

}

Ответы [ 2 ]

0 голосов
/ 14 июня 2019

Вам необходимо добавить весь код внутри метода 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)
    }
}

OR

Вы можете перейти со статическим значением 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)
}

Примечание:

Вы получаете непредсказуемый результат, потому что добавляете слой перед рендерингом кадров.

0 голосов
/ 14 июня 2019

Переместите код, опираясь на кадр .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
}
...