Natasha
Natasha

Reputation: 6903

When CAAnimationGroup's execution is completed, it goes back to initial animation sequence with a jerking effect

Here, I am creating a vanishing circular layer animation. My code is following:

import UIKit

class VanishingLoader: UIView {
    
    private let loaderLayer = CAShapeLayer()
    private var layerPaths = [UIBezierPath]()
   
    private let animationframeDuration = 0.25
    private var lastFrameDuration = 0.0
    weak var parentView: UIView?
       
    var parenViewCenter: CGPoint?{
      if let frame = parentView?.bounds{
         return CGPoint(x: frame.midX, y: frame.midY)
      }
      return nil
    }
    let animationGroup = CAAnimationGroup()
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
    }
    
    init(onView view: UIView) {
        super.init(frame: view.frame)
        parentView = view
        layerPaths = preparedPathList
        if layerPaths.count > 0 {
            let path = layerPaths[0]
            addVanishingStrokeLayer(path, withStrokeColor: UIColor.systemGreen, andStrokeWidth: nil)
        }
    }
 
    private func addVanishingStrokeLayer(_ path: UIBezierPath, withStrokeColor strokeColor:UIColor?, andStrokeWidth strokeWidth: CGFloat?){
        
        let initialPath = layerPaths[0].cgPath
        loaderLayer.path = initialPath
        loaderLayer.fillColor = UIColor.clear.cgColor
      
        if let loaderLayerColor = strokeColor{
            loaderLayer.strokeColor = loaderLayerColor.cgColor
        }else{
           loaderLayer.strokeColor = defaultStrokeColor.cgColor
        }
       
        if let lineWidth = strokeWidth{
           loaderLayer.lineWidth = lineWidth
        }else{
           loaderLayer.lineWidth = 5.0
        }
        loaderLayer.add(vanishingAnimationGroup(), forKey: "vanishingGroupAnimation")
        self.layer.addSublayer(loaderLayer)
    }


    var preparedPathList: [UIBezierPath]{
        var pathList = [UIBezierPath]()
        var multiplier : CGFloat = 0.3
        for _ in 0..<8 {
          if let path = getPathForLayer(withMultiplier: multiplier){
              pathList.append(path)
              multiplier += 0.10
          }
        }
        return pathList
    }
    
    private func getPathForLayer(withMultiplier multiplier: CGFloat = 1.0)->UIBezierPath?{
         let radius = 5.0
         let path = UIBezierPath(arcCenter: .zero , radius: radius, startAngle: 0, endAngle: 4 * CGFloat.pi, clockwise: true)
         return path
    }
    
    
    private func prepareForwardAnimationFrame(forPathCount count: Int)->CASpringAnimation{
       
        let initialPath = layerPaths[count]
        let finalPath = layerPaths[count + 1]
        
        let pathAnimation = CASpringAnimation(keyPath: "path")
        pathAnimation.fromValue = initialPath.cgPath
        pathAnimation.toValue = finalPath.cgPath
        pathAnimation.initialVelocity = 1
        pathAnimation.damping = 5
        pathAnimation.beginTime = lastFrameDuration
        pathAnimation.duration = animationframeDuration
        
        
        return pathAnimation
    }

    
    private func vanishingAnimationGroup()->CAAnimationGroup{

        var animationPaths = [CASpringAnimation]()

        for count in 0..<(layerPaths.count - 1){
            let anim = prepareForwardAnimationFrame(forPathCount: count)
            animationPaths.append(anim)
            lastFrameDuration = anim.beginTime + animationframeDuration
        }

        animationGroup.animations = animationPaths
        animationGroup.duration = (Double(animationPaths.count) * animationframeDuration)
        animationGroup.repeatCount = .infinity
        animationGroup.fillMode = .forwards
        animationGroup.isRemovedOnCompletion = false
        return animationGroup
    }
    
    public func hide(){
         self.removeFromSuperview()
     }
    
     public func show(){
         if let view = parentView{
           view.addSubview(self)
         }
     }
}

From the ViewController, have to add the following two lines to see the effect.

let loader = VanishingLoader(onView: placeHolderImageView)
loader.show()

The problem, I am facing is when the animation sequence finishes, it goes back to the initial path which is the smallest of the seven paths and that gives a sudden jerking effect. Please help me.

Upvotes: -1

Views: 158

Answers (1)

matt
matt

Reputation: 535925

First of all, this should be a key frame animation, not a grouped animation. Second, the change from last to first doesn’t animate because you didn’t animate it. Add another key frame that performs the missing animation.

I rewrote your animation as a key frame animation, and so far, I have this, but I don't think it is what you want, so please say more clearly what the intended effect is supposed to be:

enter image description here

Upvotes: 1

Related Questions