IOS自定义进度条与CAShapeLayer

Posted

技术标签:

【中文标题】IOS自定义进度条与CAShapeLayer【英文标题】:IOS custom progress bar with CAShapeLayer 【发布时间】:2021-04-14 11:19:22 【问题描述】:

如何用弧尾的文字创建像这张图片一样的自定义弓

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

        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
         

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

【问题讨论】:

github.com/EranBoudjnah/MTCircularSlider/blob/master/… 【参考方案1】:

让您开始...

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

弧线从.pi 开始,即“9 点” 完整进度将跨越到“3 点钟”,即添加 .pi 到开始角度

完成进度为.pi .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
    

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

这是您的代码,稍作修改以添加 score 值和 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

    

结果:

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

【讨论】:

以上是关于IOS自定义进度条与CAShapeLayer的主要内容,如果未能解决你的问题,请参考以下文章

Android自定义View实现可拖拽的进度条

进度条与拖动条的使用学习

将进度条与中心对齐

进度条与HttpClient

C sharp #004# 进度条与Timer

进度条与时间轴绑定显示图片