Search code examples
iosswiftspringcore-animation

Spring animation for rotation that ends where it began


I'd like to create a CASpringAnimation for rotation that ends where it began. Here's some working code that doesn't end where it began (but otherwise is what I'm looking for):

func spring(view: UIView) -> CASpringAnimation{
    let animation = CASpringAnimation(keyPath: "transform")
    animation.damping = 0.5
    animation.initialVelocity = 70
    animation.mass = 0.04
    animation.stiffness = 12
    animation.duration = 1.2
    animation.toValue = CATransform3DRotate(CATransform3DMakeAffineTransform(view.transform),
                                            CGFloat(M_PI)/32, // Must be non-zero 😕
                                            0, 0, 1)
    return animation
}

And here's how you'd use it:

    let animation = spring(view: viewToAnimate)
    viewToAnimate.layer.add(animation, forKey: nil)

When I set the angle (beside the comment in CATransform3DRotate) to 0, the animation doesn't work! Any ideas on how to get a spring rotation with the same start and end angles? Thanks for reading.


Solution

  • By adding a CABasicAnimation that animates to 0° over the same time duration, and also by tweaking the spring animation parameters, I've got something that looks pretty good (tested for iOS 10):

    @IBAction func springButtonTapped(_ sender: UIButton) { // Attach to the view you'd like animated
        let springAnim = spring(view:sender)
        sender.layer.add(springAnim, forKey: nil)
    
        let correctionAnim = correct(view:sender)
        sender.layer.add(correctionAnim, forKey: nil)
    }
    
    func correct(view: UIButton) -> CABasicAnimation{
        let animation = CABasicAnimation(keyPath: "transform")
        animation.duration = 0.3
        animation.toValue = CATransform3DRotate(CATransform3DMakeAffineTransform(view.transform),
                                                0,
                                                0, 0, 1)
        return animation
    }
    
    func spring(view: UIView) -> CASpringAnimation{
        let animation = CASpringAnimation(keyPath: "transform")
        animation.damping = 0.0
        animation.initialVelocity = 80
        animation.mass = 0.04
        animation.stiffness = 50
        animation.duration = 0.3
        animation.toValue = CATransform3DRotate(CATransform3DMakeAffineTransform(view.transform),
                                                CGFloat(M_PI)/64,
                                                0, 0, 1)
        return animation
    }