how to set animation curve when using keyframe ani

2019-04-04 17:55发布

问题:

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:

回答1:

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;
}


回答2:

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.



回答3:

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.



回答4:

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

extension UIViewKeyframeAnimationOptions {

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

}

Usage:

UIViewKeyframeAnimationOptions(animationOptions: .CurveEaseOut)


回答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)