• Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.

The final effect to be completed:

The first thing to do is draw a circle with the CAShapeLayer. Here you create a path by placing the center of the circle in the center of the view with a radius of 100, then setting the starting Angle and the ending Angle, which is shown by setting true. Then set the path of the shapeLayer to the path you just created, and finally add the shapeLayer to the Layer sublayer of the View.

 let shapeLayer = CAShapeLayer()
        
        let center = view.center
        let circularPath = UIBezierPath(arcCenter: center, radius: 100, startAngle: 0, endAngle: CGFloat.pi * 2, clockwise: true)
        shapeLayer.path = circularPath.cgPath
            
        view.layer.addSublayer(shapeLayer)
Copy the code

Next add a red circle around the shapeLayer.

 shapeLayer.strokeColor = UIColor.red.cgColor
 shapeLayer.lineWidth = 10
Copy the code

The effect is as follows:

Next, add a click gesture to the view to animate the outer circle later.

view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleTap)))
Copy the code

Add a response method

@objc func handleTap() {
}
Copy the code

You need a shapeLayer to animate the outer circle, so put the shapeLayer outside and let the VC hold it. So in viewDidLoad I’m going to set shapeLayer’s strokeEnd to 0

 shapeLayer.strokeEnd = 0
Copy the code

Then animate the shapeLayer in handleTap.

let basicAnimation = CABasicAnimation(keyPath: "strokeEnd")
        basicAnimation.toValue = 1
        basicAnimation.duration = 2
        shapeLayer.add(basicAnimation, forKey: "stokeAnimation")
Copy the code

So when you click on it, you draw a circle.

The problem is that the starting position should be above the circle instead of right, so you need to change the startAngle of circularPath so that the animation will start above the circle.

  let circularPath = UIBezierPath(arcCenter: center, radius: 100, startAngle: -CGFloat.pi / 2, endAngle: CGFloat.pi * 2, clockwise: true)
Copy the code

Another problem here is that the head of the circle is straight instead of circular, so you just need to change the shapeLayer lineCap.

 shapeLayer.lineCap = .round
Copy the code

Now remove the black in the middle of the circle and change the shapeLayer fill color to colorless.

 shapeLayer.fillColor = UIColor.clear.cgColor
Copy the code

Next, add a track layer to the progress bar.

let trackLayer = CAShapeLayer() trackLayer.path = circularPath.cgPath trackLayer.strokeColor = UIColor.lightGray.cgColor  trackLayer.lineWidth = 10 trackLayer.fillColor = UIColor.clear.cgColor trackLayer.lineCap = .roundCopy the code

Now look like this:

Complete code:

import UIKit

class ViewController: UIViewController {
    
    let shapeLayer = CAShapeLayer()

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        
        let center = view.center
        let circularPath = UIBezierPath(arcCenter: center, radius: 100, startAngle: -CGFloat.pi / 2, endAngle: CGFloat.pi * 2, clockwise: true)
        
        let trackLayer = CAShapeLayer()
        trackLayer.path = circularPath.cgPath
        trackLayer.strokeColor = UIColor.lightGray.cgColor
        trackLayer.lineWidth = 10
        trackLayer.fillColor = UIColor.clear.cgColor
        trackLayer.lineCap = .round
            
        view.layer.addSublayer(trackLayer)
        shapeLayer.path = circularPath.cgPath
        shapeLayer.strokeEnd = 0
        shapeLayer.strokeColor = UIColor.red.cgColor
        shapeLayer.lineWidth = 10
        shapeLayer.fillColor = UIColor.clear.cgColor
        shapeLayer.lineCap = .round
            
        view.layer.addSublayer(shapeLayer)
        view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleTap)))
    }

    @objc func handleTap() {
        let basicAnimation = CABasicAnimation(keyPath: "strokeEnd")
        basicAnimation.toValue = 1
        basicAnimation.duration = 2
        basicAnimation.fillMode = .forwards
        basicAnimation.isRemovedOnCompletion = false
        shapeLayer.add(basicAnimation, forKey: "stokeAnimation")
        
    }

}

Copy the code