5

I'm experimenting with animating CAShapeLayer paths with CASpringAnimation. The expected result is a 'morphing' between shapes that acts 'springy'.

I have a basic code example between a circle and square path as below, however the end result is a spring animation that does not 'overshoot' past the final, larger square path, which is the expected behaviour.

enter image description here

My code is:

let springAnimation = CASpringAnimation(keyPath: "path")
springAnimation.damping = 1
springAnimation.duration = springAnimation.settlingDuration
springAnimation.fromValue = standardCirclePath().cgPath
springAnimation.toValue = standardSquarePath().cgPath

circleLayer.add(springAnimation, forKey: nil) // Where circleLayer (red background) is a sublayer of a basic UIView in the frame (blue background)

I got my paths from this answer.

Is there a way with CASpringAnimation to achieve this for a CAShapeLayer path transform? Otherwise, what are the alternatives?

Community
  • 1
  • 1
Sarreph
  • 1,986
  • 2
  • 19
  • 41
  • 1
    As far as I know, `CASpringAnimation` cannot be used to overshoot or undershoot path animations. This is a non-trivial problem, and Core Animation doesn't seem to have any sophisticated support for automatic path interpolation. To solve this, I'm afraid you're going to have to add some path keyframes for the overshoot and undershoot stops in your animation. – CIFilter Aug 16 '16 at 02:05
  • I believe @CIFilter is totally correct. in a word, CASpringAnimation simply **does not work** with paths. Even simple ones such as expanding circles. I think that'sthe size of it. – Fattie May 03 '23 at 17:20

2 Answers2

2

Hello I had been working on your question and this is my results, the glitches on the gif is because my gif converter is very bad converter

enter image description here

this is the code

class ViewController: UIViewController {

    @IBOutlet weak var animatableVIew: UIView!
    var isBall = false
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }
    @IBAction func startAnimation(sender: AnyObject) {

        isBall = !isBall

        let springAnimation = CASpringAnimation(keyPath: "cornerRadius")
        let halfWidth = animatableVIew.frame.width / 2
        let cornerRadius: CGFloat = isBall ? halfWidth : 0
        springAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)
        springAnimation.fromValue = isBall ? 0 : halfWidth ;
        springAnimation.toValue = cornerRadius;
        springAnimation.damping = 7
        springAnimation.duration = springAnimation.settlingDuration
        animatableVIew.layer.cornerRadius = cornerRadius

        let springAnimation2 = CAKeyframeAnimation(keyPath: "transform.scale")
        springAnimation2.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
        springAnimation2.values = [1,0.9,1.4,0.8,1]
        springAnimation2.keyTimes = [ 0, (1 / 6.0), (3 / 6.0), (5 / 6.0), 1 ];
        springAnimation2.duration = springAnimation.settlingDuration * 0.5

        let groupSpringAnimation = CAAnimationGroup()
        groupSpringAnimation.animations = [springAnimation,springAnimation2]
        groupSpringAnimation.duration = springAnimation.settlingDuration
        groupSpringAnimation.beginTime = 0;
        animatableVIew.layer.addAnimation(groupSpringAnimation, forKey: "group")

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


}

I hope this helps you

Reinier Melian
  • 20,519
  • 3
  • 38
  • 55
0

The fact is:

CASpringAnimation simply does not work with paths.

And that's that.

Even simple paths such as expanding circles.

This is still true as of 2023, and unfortunately that's all there is to it.

As @CIFilter points out in a comment, generally expanding the path "bigger" is a non-trivial problem, they simply have not solved.

Fattie
  • 27,874
  • 70
  • 431
  • 719