how to set animation curve when using keyframe animation in ios?
Asked Answered
T

6

21

How to set animation curve when using UIView's keyframe animation :

animateKeyframesWithDuration:delay:options:animations:completion:

Whatever I do in the animation block seems to be linear (unless I use the UIViewKeyframeAnimationOptionCalculationModeCubic option but this isn't what I want).

I'd like to have an ease out curve on the animation like the UIViewAnimationOptionCurveEaseOut option when using regular animation :

animateWithDuration:delay:options:animations:completion:
Trumpetweed answered 17/12, 2013 at 16:4 Comment(0)
A
2

If you are using keyframes, you have to define the curve on your own.. if you add linear keyframes, you have a linear animation. If you add non-linear keyframes, you will have a non-linear animation.

The frameStartTime is your friend here... it will always be linear between keyframes (or paced / cubic / cubic paced, like defined in the UIViewKeyframeAnimationOptionCalculationMode)

UIViewKeyframeAnimationOptionCalculationModeLinear     = 0 << 9,
UIViewKeyframeAnimationOptionCalculationModeDiscrete   = 1 << 9,
UIViewKeyframeAnimationOptionCalculationModePaced      = 2 << 9,
UIViewKeyframeAnimationOptionCalculationModeCubic      = 3 << 9,
UIViewKeyframeAnimationOptionCalculationModeCubicPaced = 4 << 9

To calculate correct timing values, you could use this as a reference: RBBEasingFunction

E.g. EaseInOutQuad like this (where t is the relative time within the animation):

if (t < 0.5) {
    return 2 * t * t;
} else {
    return -1 + (4 - 2 * t) * t;
}
Airspace answered 17/12, 2013 at 16:32 Comment(0)
K
27

Swift 2.0 will not allow casting of UIViewAnimationOptions as UIViewKeyframeAnimationOptions. It will also not allow |ing them together.

However, there is an initializer for UIViewKeyframeAnimationOptions that takes a rawValue. So, the following code works to put UIViewAnimationOptions into UIViewKeyframeAnimationOptions:

let animationOptions: UIViewAnimationOptions = .CurveEaseOut
let keyframeAnimationOptions: UIViewKeyframeAnimationOptions = UIViewKeyframeAnimationOptions(rawValue: animationOptions.rawValue)

Then I can pass keyframeAnimationOptions to animateKeyframesWithDuration and everything works great.

For Swift 4:

let animationOptions = AnimationOptions.curveEaseOut
let options = KeyframeAnimationOptions(rawValue: animationOptions.rawValue)
Kortneykoruna answered 13/12, 2015 at 20:55 Comment(2)
How can I increase the .CurveEaseOut effect? Thanks, nice answer!Razzia
This doesn't seem to work in swift3 and higher. The animation still appears linear. Has anyone found a solution that works in swift4?Wagonage
P
13

Actually, you can use the same curve flags that you use for animateWithDuration:delay:options:animations:completion:. Just "binary or" them in with the other options in your call to animateKeyframesWithDuration:delay:options:animations:completion:

The documentation is confusing, but it does work.

The curve values you can use are:

UIViewAnimationOptionCurveEaseInOut = 0 << 16,
UIViewAnimationOptionCurveEaseIn    = 1 << 16,
UIViewAnimationOptionCurveEaseOut   = 2 << 16,
UIViewAnimationOptionCurveLinear    = 3 << 16,

The default animation curve you get is 0, or ease-in, ease-out.

Potvaliant answered 20/2, 2014 at 1:21 Comment(5)
Awesome! works fine. Very weird this isn't in the documentation, specially since they have non-linear as default...Olva
Well, after trying all the KeyFrameOptions I went back to this which is what I had to begin with. It gives a warning but works perfectly :)Conqueror
This won't work with Swift because everything is strongly typed!Hobbema
@fatuhoku, you should be able to overcome that with typecastingPotvaliant
In Swift 4 for UIViewAnimationOptionCurveLinear it'd be: UIViewKeyframeAnimationOptions(rawValue: (3 << 16))Suppletory
C
11

Here's a nice and clean Swift solution I came up with:

extension UIViewKeyframeAnimationOptions {

    init(animationOptions: UIViewAnimationOptions) {
        rawValue = animationOptions.rawValue
    }

}

Usage:

UIViewKeyframeAnimationOptions(animationOptions: .CurveEaseOut)
Cerous answered 18/1, 2016 at 16:11 Comment(1)
Swift 5: self.init(rawValue: animationOptions.rawValue)Opportunity
C
5

The answer by @eskimwier did not work for me when trying to set the curve to linear for animateKeyframes(withDuration:delay:options:animations:completion:). This was in Swift 3, Xcode 8.2.1.

My animation appeared to be defaulting to easeInOut (slow start up, faster in the middle, slow at the end). Did Apple change the default curve?

Anyhow, I wanted a linear curve, and tried the approach suggested above to use UIViewAnimationOptions with keyframe animations:

let animationOptions: UIViewAnimationOptions = .curveLinear
var keyframeAnimationOptions: UIViewKeyframeAnimationOptions = UIViewKeyframeAnimationOptions(rawValue: animationOptions.rawValue)

UIView.animateKeyframes(withDuration: 3, delay: 0, options: [keyframeAnimationOptions], animations: {
    //... animations here ...
}, completion: nil)

This had no effect. The only thing that worked for me was to do this before calling animateKeyframes:

UIView.setAnimationCurve(UIViewAnimationCurve.linear)
Conlin answered 3/5, 2017 at 20:43 Comment(1)
I tried both of these suggestions. The first option (with the keyframeAnimationOptions was the one that worked for me.Document
A
2

If you are using keyframes, you have to define the curve on your own.. if you add linear keyframes, you have a linear animation. If you add non-linear keyframes, you will have a non-linear animation.

The frameStartTime is your friend here... it will always be linear between keyframes (or paced / cubic / cubic paced, like defined in the UIViewKeyframeAnimationOptionCalculationMode)

UIViewKeyframeAnimationOptionCalculationModeLinear     = 0 << 9,
UIViewKeyframeAnimationOptionCalculationModeDiscrete   = 1 << 9,
UIViewKeyframeAnimationOptionCalculationModePaced      = 2 << 9,
UIViewKeyframeAnimationOptionCalculationModeCubic      = 3 << 9,
UIViewKeyframeAnimationOptionCalculationModeCubicPaced = 4 << 9

To calculate correct timing values, you could use this as a reference: RBBEasingFunction

E.g. EaseInOutQuad like this (where t is the relative time within the animation):

if (t < 0.5) {
    return 2 * t * t;
} else {
    return -1 + (4 - 2 * t) * t;
}
Airspace answered 17/12, 2013 at 16:32 Comment(0)
H
2

A simple way to set the animation curve for the keyframe animation is to wrap the keyframe animation inside the UIViewPropertyAnimator object:

let animator = UIViewPropertyAnimator(duration: 0.5, curve: .linear) {

    // withDuration: 0 means inherited duration from UIViewPropertyAnimator
    UIView.animateKeyframes(withDuration: 0, delay: 0, animations: {
        UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration:0.5) {
            // modify view's properties
        }

        // more keyframes
    })
}

animator.startAnimation()
Haddington answered 30/9, 2020 at 16:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.