-1

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.

Natasha
  • 6,651
  • 3
  • 36
  • 58

1 Answers1

1

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

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Can you provide some code. I have updated my code with your suggestion but it didn't work at all. Now it doesn't even animate. Please check my updated post. – Natasha Aug 10 '20 at 15:40
  • Because that isn't how you do a key frame animation. The key times are _fractions_. – matt Aug 10 '20 at 15:59
  • I added the keyTimes array with the fraction of the times, that I want as duration for each keyframe. Still nothing. Updated my answer with your suggestion.. – Natasha Aug 10 '20 at 16:58
  • To make it focused, I posted the main part. As it is not clear enough to you, I am updating my question with whole custom class. – Natasha Aug 10 '20 at 17:33
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/219566/discussion-between-natasha-and-matt). – Natasha Aug 10 '20 at 19:29
  • Okay, I was able to rewrite as a keyframe animation. But now, please say clearly what you want. You say "it goes back to the initial path which is the smallest of the seven paths and that gives a sudden jerking effect". Yes, because you said to repeat. So we start over at the beginning, which is the initial path. If that is not what you want, what _do_ you want? – matt Aug 11 '20 at 13:55
  • Also, do you really like the way your animation jumps from path to path? Would you rather have a single smooth animation? Please describe exactly what you want. – matt Aug 11 '20 at 13:58
  • I couldn't make it work with keyframe animation. I shared my code. Can you correct my KeyframeAnimation part, please. – Natasha Aug 11 '20 at 21:31
  • Yes, that's what I want to do. Please listen to what I am asking you. Tell me what the final effect should be, and I will show the code that gives that effect. I cannot tell from your code what you want or what is wrong with what you are getting. Tell me _in words_ exactly how the animation should behave. – matt Aug 11 '20 at 21:45
  • I want to start from a smaller radius circle and that should animate to a 10 times larger one and eventually become 5 or six times larger one by one. When a new circle appears, I want the previous one fade out behind that. Also this should keep happening repeatedly, until I stop it. That's it. – Natasha Aug 13 '20 at 11:15
  • Yes but you need to explain clearly. These words are meaningless: “animate to a 10 times larger one and eventually become 5 or six times larger one by one”. 10? 5? 6? One by one? Huh? Please describe or illustrate exactly. I cannot follow nonsensical instructions. As for “fade”, I don’t see any fade in your code, so I can’t guess what you mean. – matt Aug 13 '20 at 12:39
  • To express it another way: Look at my solution. What do you want me to change about it? – matt Aug 13 '20 at 12:40
  • Ok, let's forget about the fade out thing as I still have to add that part. Now, if you have wrote your animation as keyFrame, the only improvement, I would like is the sudden re-starting of the animation. The animation is going to repeat. The time between the end of one full animation cycle and re-starting again to repeat is very small which gives a sudden jerking effect. Also, I tried to change my animationGroup to keyFrameAnimation and it didn't work at all. So, what did I do wrong? – Natasha Aug 13 '20 at 13:36
  • Be patient, I'll show you the code after I get it to do what you want done! - Okay, so are you saying you like the effect I'm getting, but you wish the pause at the end of each growth was longer? – matt Aug 13 '20 at 13:46
  • Yes, that's fine. I will combine a fading out effect for each frame after my initial issue gets fixed. Yours almost have the same effect as of mine, so for now just need to fix the sudden jerking effect. – Natasha Aug 13 '20 at 17:53
  • But with my code we will still jerk to the smallest size; it's just that the delay will be longer. However, I can instead quickly _animate_ to the smaller size; that is _why_ I recommended we write this as a keyframe animation. Would you prefer that? – matt Aug 13 '20 at 18:13
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/219806/discussion-between-natasha-and-matt). – Natasha Aug 14 '20 at 10:38