首页 > 解决方案 > IOS自定义进度条与CAShapeLayer

问题描述

我怎样才能用弧结束的文字创建一个像这张图片的自定义弓

彩虹加载图像

这是我当前的代码和当前结果

        let center = view.center
        let circularPath = UIBezierPath(arcCenter: center, radius: 120, startAngle:  CGFloat.pi , endAngle: CGFloat.pi*2, clockwise: true)

        
        let greyLayer = CAShapeLayer()
        greyLayer.strokeColor = greyColor
        greyLayer.lineWidth = lineWidth
        greyLayer.path = circularPath.cgPath
        greyLayer.lineCap = .round
        greyLayer.fillColor = UIColor.clear.cgColor
        greyLayer.shadowColor = UIColor.black.cgColor
        greyLayer.shadowOpacity = 1
        greyLayer.shadowOffset = .zero
        greyLayer.shadowRadius = 2
        view.layer.addSublayer(greyLayer)


        shapeLayer.strokeColor = bowColor
        shapeLayer.lineWidth = lineWidth
        shapeLayer.path = circularPath.cgPath
        shapeLayer.lineCap = .round
        shapeLayer.strokeEnd =  0
        shapeLayer.fillColor = UIColor.clear.cgColor
        view.layer.addSublayer(shapeLayer)
        view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleTap)))

        let label = UILabel()
        label.text = "Best"
        label.textAlignment = .center
        label.textColor = .red
        label.font = UIFont.boldSystemFont(ofSize: 30)
        
                
        view.addSubview(label)
        
        view.layer.addSublayer(label.layer)
        label.translatesAutoresizingMaskIntoConstraints = false
        
        label.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
        
        label.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
        
        label.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive  = true
        
        label.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
         

当前结果我想在弧的末端添加一个圆形并像上图一样动画,我不知道如何实现这个 在此处输入图像描述

标签: iosswiftviewprogress

解决方案


为了让你开始...

您的弧线从.pi.pi*2... 但.pi*2它可能会帮助您将其视为:

  • 弧从 开始.pi,即“9 点钟”
  • 完成进度将跨越到“3 点钟”,这增加 .pi了开始角度

完成进度将是.pi plus .pi * progressPercent

因此,要获得endAngle

  • 25% 的进展将是.pi + ((25.0 / 100.0) * .pi)
  • 50% 的进展将是.pi + ((50.0 / 100.0) * .pi)
  • 83% 的进展将是.pi + ((83.0 / 100.0) * .pi)

编码,你会做这样的事情:

    let score = 83
    
    let endAngle: CGFloat = .pi + ((CGFloat(score) / 100.0) * .pi)
    
    let center = view.center
    let circularPath = UIBezierPath(arcCenter: center, radius: 120, startAngle: .pi, endAngle: endAngle, clockwise: true)

现在你的弧线跨越了 83% 的“半圆”。

要在弧的末端添加标签,您可以从路径中获取该点:

    // point at end of arc
    let endPoint: CGPoint = circularPath.currentPoint
    

您可以使用该点来定位您的标签(或自定义“气球”标签视图)。

这是您的代码,稍作修改以添加scorevalue 和 a progressLabel

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = .systemTeal
        
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        
        let greyColor = UIColor.gray.cgColor
        let bowColor = UIColor.systemPink.cgColor
        let lineWidth: CGFloat = 12
        let shapeLayer = CAShapeLayer()
        
        let score = 83
        
        let endAngle: CGFloat = .pi + (.pi * (CGFloat(score) / 100.0))
        
        let center = view.center
        let circularPath = UIBezierPath(arcCenter: center, radius: 120, startAngle: .pi, endAngle: endAngle, clockwise: true)

        // point at end of arc
        let endPoint: CGPoint = circularPath.currentPoint
        
        let greyLayer = CAShapeLayer()
        greyLayer.strokeColor = greyColor
        greyLayer.lineWidth = lineWidth
        greyLayer.path = circularPath.cgPath
        greyLayer.lineCap = .round
        greyLayer.fillColor = UIColor.clear.cgColor
        greyLayer.shadowColor = UIColor.black.cgColor
        greyLayer.shadowOpacity = 1
        greyLayer.shadowOffset = .zero
        greyLayer.shadowRadius = 2
        view.layer.addSublayer(greyLayer)
        
        
        shapeLayer.strokeColor = bowColor
        shapeLayer.lineWidth = lineWidth
        shapeLayer.path = circularPath.cgPath
        shapeLayer.lineCap = .round
        shapeLayer.strokeEnd =  0
        shapeLayer.fillColor = UIColor.clear.cgColor
        view.layer.addSublayer(shapeLayer)
        //view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleTap)))
        
        let label = UILabel()
        label.text = "Best"
        label.textAlignment = .center
        label.textColor = .red
        label.font = UIFont.boldSystemFont(ofSize: 30)
        
        
        view.addSubview(label)
        
        view.layer.addSublayer(label.layer)
        label.translatesAutoresizingMaskIntoConstraints = false
        
        label.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
        
        label.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
        
        label.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive  = true
        
        label.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
        
        let progressLabel = UILabel()
        progressLabel.backgroundColor = .cyan
        progressLabel.text = "\(score)%"
        progressLabel.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(progressLabel)
        progressLabel.bottomAnchor.constraint(equalTo: view.topAnchor, constant: endPoint.y).isActive = true
        progressLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: endPoint.x).isActive = true

    }
}

结果:

在此处输入图像描述

如果你想“动画化”进度弧和标签,那将需要更多的工作......但这是你学习的一个很好的练习。


推荐阅读