29

After playing around a lot with the UIView dynamic animations introduced in iOS 7, most notably:

[UIView animateWithDuration: delay: usingSpringWithDamping: initialSpringVelocity: options: animations: completion:];

I was wondering if there is an equivalent to 'SpringWithDamping/Velocity' method that can be accessed directly when creating a CALayer animation? I.e. either through CATransaction, CABasicAnimation or otherwise...

Thanks

Sarreph
  • 1,986
  • 2
  • 19
  • 41

4 Answers4

77

in iOS9 Apple finally made the CASpringAnimation class public.

You can use it like that:

let spring = CASpringAnimation(keyPath: "position.x")
spring.damping = 5
spring.fromValue = myLayer.position.x
spring.toValue = myLayer.position.x + 100.0
spring.duration = spring.settlingDuration
myLayer.addAnimation(spring, forKey: nil)

Notice that you cannot set the animation duration - you need to ask the CASpringAnimation class for the settlingDuration (e.g. "How much time is going to take for the spring system to settle down") and then set it as the duration of your animation.

Check the header files for CASpringAnimation - it exposes a number of spring system variables you can adjust - stiffness, mass, etc.

Marin Todorov
  • 6,377
  • 9
  • 45
  • 73
  • 4
    "Notice that you cannot set the animation duration" – you actually can set it to any value, but if it's too short it may abruptly stop before settling so if you don't want to do the guess work yourself `settlingDuration` is the way to go. – Bartosz Ciechanowski Jul 10 '15 at 03:50
6

There is (and have been for a while) a private class called CASpringAnimation that I'm pretty sure is being used behind it all (but I haven't verified it). Unfortunately, it is still private.

David Rönnqvist
  • 56,267
  • 18
  • 167
  • 205
  • Thanks @David, this is what I found also, but as it is private I don't want to use it in deployment. — Do you happen to have any personal experience in any other libraries/classes that achieve the same effect? Thanks! – Sarreph Mar 11 '14 at 18:05
  • @Rorz No, I don't have any experience with other libraries. Sorry – David Rönnqvist Mar 11 '14 at 18:24
4

As David said, CASpringAnimation is private (for now?), but I recently came across RBBSpringAnimation from the RBBAnimation project.

I can definitely recommend this, it was very easy to drop in as a replacement for my existing CABasicAnimation.

dmur
  • 530
  • 3
  • 14
  • Agreed, using `RBBSpringAnimation` is an accurate replacement for CASpringAnimation. Useful if you are trying to support iOS 7 and 8. – brynbodayle Jan 29 '16 at 19:55
1

I wrote a class to create CASpringAnimation instance. It works in a pretty simple way:

By creating a spring animation from UIKit API, it arrests the created CASpringAnimation instance from the view's layer, copies it and returns it.

But I don't know if it is App Store safe that to create CASpringAnimation in this way.

import UIKit

private let SharedCASpringAnimationFactory = CASpringAnimationFactory()

public class CASpringAnimationFactory {
    private var dummyView: UIView

    private init() {
        dummyView = UIView(frame: CGRect.zeroRect)
    }

    private class var shared: CASpringAnimationFactory {
        return SharedCASpringAnimationFactory
    }


     public class func animation(#keyPath: String, dumping: CGFloat, initialSpringVelocity: CGFloat) -> CABasicAnimation {
        let duration = CATransaction.animationDuration()

        UIView.animateWithDuration(duration, delay: 0.0, usingSpringWithDamping: dumping, initialSpringVelocity: initialSpringVelocity, options: nil,
            animations: { () -> Void in
                CASpringAnimationFactory.shared.dummyView.bounds = CGRect(origin: CGPoint.zero, size: CGSize(width: 100, height: 100))
            }, completion: nil)

        let dummyLayer = CASpringAnimationFactory.shared.dummyView.layer

        let animations = dummyLayer.animationKeys().map {dummyLayer.animationForKey($0 as String) as CAAnimation}

        let arrestedAnimation = animations.first!.copy() as CABasicAnimation
        arrestedAnimation.keyPath = keyPath
        arrestedAnimation.fromValue = nil
        arrestedAnimation.toValue = nil

        dummyLayer.removeAllAnimations()
        shared.dummyView.bounds = CGRect.zeroRect

        return arrestedAnimation
    }
}
WeZZard
  • 3,536
  • 1
  • 23
  • 26