0

I am using an arc for a progress bar.

enter image description here

There is a track layer that the progress indicator moves along and I also have a circle that moves with the progress bar for added emphasis. Both the progress bar and the circle move correctly along the path but when the progress is complete, instead of stopping at the end of the path, the circle jumps back to the beginning of the progress path.

screen recording of behavior

The code:

class ViewController: UIViewController {
    
    let shapeLayer = CAShapeLayer()
    let trackLayer = CAShapeLayer()
    let dotLayer: CALayer = CALayer()
    let seconds = 10.0
    
    // MARK: - Properties
    private var progressBarView = UIView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        title = "Making Progress"
        view.backgroundColor = .systemTeal
        
        // determine the value of Center
        let center = view.center
        
        let circularPath = UIBezierPath(arcCenter: center, radius: 100, startAngle: 2.7, endAngle: 0.45, clockwise: true) // these values give us a complete circle
        
        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.strokeColor = UIColor.red.cgColor
        shapeLayer.lineWidth = 10
        shapeLayer.strokeEnd = 0
        shapeLayer.fillColor = UIColor.clear.cgColor
        shapeLayer.lineCap = .round
        
        view.layer.addSublayer(shapeLayer)
        
        view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleTap )))
    }
    
    @objc private func handleTap() {
        print("Attempting to animate stroke")
        
        let basicAnimation = CABasicAnimation(keyPath: "strokeEnd")
        
        basicAnimation.toValue = 1
        
        basicAnimation.duration = seconds
        
        basicAnimation.fillMode = CAMediaTimingFillMode.forwards
        basicAnimation.isRemovedOnCompletion = false
        
        shapeLayer.add(basicAnimation, forKey: "anyKeyWillDo")
        
        // circle image
        let circleNobImage = UIImage(named: "whiteCircle.png")!
        
        dotLayer.contents = circleNobImage.cgImage
        dotLayer.bounds = CGRect(x: 0.0, y: 0.0, width: circleNobImage.size.width, height: circleNobImage.size.height)
        
        view.layer.addSublayer(dotLayer)
        
        dotLayer.position = CGPoint(x: 105, y: 460)
        
        dotLayer.opacity = 1
        
        dotAnimation()
    }
    
    func dotAnimation() {
        let dotAnimation = CAKeyframeAnimation(keyPath: "position")
        dotAnimation.path = trackLayer.path
        dotAnimation.calculationMode = .paced
        
        let dotAnimationGroup = CAAnimationGroup()
        dotAnimationGroup.duration = seconds
        
        dotAnimation.autoreverses = false
        dotAnimationGroup.animations = [dotAnimation]
        dotLayer.add(dotAnimationGroup, forKey: nil)
    }
}

I need to get the circle to stop at the end of the arc with the progress bar. How is this done?

RTXGamer
  • 3,215
  • 6
  • 20
  • 29
forrest
  • 10,570
  • 25
  • 70
  • 132

1 Answers1

1

Also set dotAnimationGroup.fillMode and dotAnimationGroup.isRemovedOnCompletion

dotAnimationGroup.fillMode = .forwards
dotAnimationGroup.isRemovedOnCompletion = false

enter image description here

Raja Kishan
  • 16,767
  • 2
  • 26
  • 52
  • That second line made all the difference. Thank you very much! – forrest Oct 06 '21 at 15:38
  • 1
    What Raja describes works, but is not a great solution. That causes the animation's "presentation layer" to stick around after the animation completes. The underlying layers are not actually moved. It's usually better to set your layers to their end state right after adding the animation. That change is hidden by the "in flight" animation", and is revealed once the animation completes and the presentation layer gets hidden. – Duncan C Oct 06 '21 at 16:51
  • @DuncanC I would appreciate additional insights on this if you would be willing. – forrest Oct 14 '21 at 06:05