UIView keyframe animations let you create view animations that proceed through a series of steps. The function animateKeyframes(withDuration:delay:options:animations:completion:)
has an options parameter of type UIView.KeyframeAnimationOptions
. I would expect that to let me select an overall timing curve to apply to the combined set of animations. It appears to apply a default animation curve of ease-in,ease-out, and does not seem to allow you to specify timing curves like the UIView.animate()
family of methods do. What if I want linear timing, just ease-in, or just ease-out?

- 128,072
- 22
- 173
- 272
1 Answers
Interestingly, the animation options for the UIView.animate()
methods (of type UIView.AnimationOptions
) work if you can figure out how to pass them to animateKeyframes(withDuration:delay:options:animations:completion:)
. Unfortunately, there are not existing constants for timing curves like linear, ease-in, or ease-out. This SO answer shows how to "force" the values from a UIView.AnimationOptions
constant into a UIView.KeyframeAnimationOptions
OptionSet:
extension UIViewKeyframeAnimationOptions {
init(animationOptions: UIViewAnimationOptions) {
rawValue = animationOptions.rawValue
}
}
I took that a step further, and defined constants for the different timing functions. (I don't know why Apple doesn't define these. It seems absurd, but there you are.)
My extension to UIView.KeyframeAnimationOptions
looks like this:
extension UIView.KeyframeAnimationOptions {
static var curveLinear: UIView.KeyframeAnimationOptions =
UIView.KeyframeAnimationOptions(rawValue:UIView.AnimationOptions.curveLinear.rawValue)
static var curveEaseInOut: UIView.KeyframeAnimationOptions =
UIView.KeyframeAnimationOptions(rawValue:UIView.AnimationOptions.curveEaseInOut.rawValue)
static var curveEaseIn: UIView.KeyframeAnimationOptions =
UIView.KeyframeAnimationOptions(rawValue:UIView.AnimationOptions.curveEaseIn.rawValue)
static var curveEaseOut: UIView.KeyframeAnimationOptions =
UIView.KeyframeAnimationOptions(rawValue:UIView.AnimationOptions.curveEaseOut.rawValue)
init(animationOptions: UIView.AnimationOptions) {
self.init(rawValue: animationOptions.rawValue)
}
}
With that extension, you can use the timing curve values just like you would in a call to the UIView.animate()
method:
UIView.animateKeyframes(withDuration: 2.0, delay: 0, options: [.curveLinear]) {
// Your animation keyframe steps here
}
Some of the other flags defined in UIView.AnimationOptions might also be valid for UIView.KeyframeAnimationOptions
. You could use the method that lets you map UIView.AnimationOptions
to UIView.KeyframeAnimationOptions
, or it would be trivial to add additional flags to UIView.KeyframeAnimationOptions
the same way I added the timing curve flags.
Edit:
I got curious about the flags in the options to UIView.animate()
and animateKeyframes()
matched up. I wrote some code to log all of the values for both OptionSets, and this is what I got:
KeyframeAnimationOptions:
option value 0x00000001 = layoutSubviews
option value 0x00000002 = allowUserInteraction
option value 0x00000004 = beginFromCurrentState
option value 0x00000008 = repeat
option value 0x00000010 = autoreverse
option value 0x00000020 = overrideInheritedDuration
option value 0x00000200 = overrideInheritedOptions
option value 0x00000000 = calculationModeLinear
option value 0x00000400 = calculationModeDiscrete
option value 0x00000800 = calculationModePaced
option value 0x00000C00 = calculationModeCubic
option value 0x00001000 = calculationModeCubicPaced
UIView.AnimationOptions:
option value 0x00000001 = layoutSubviews
option value 0x00000002 = allowUserInteraction
option value 0x00000004 = beginFromCurrentState
option value 0x00000008 = repeat
option value 0x00000010 = autoreverse
option value 0x00000020 = overrideInheritedDuration
option value 0x00000200 = overrideInheritedOptions
option value 0x00000040 = overrideInheritedCurve
option value 0x00000080 = allowAnimatedContent
option value 0x00000100 = showHideTransitionViews
option value 0x00000000 = curveEaseInOut
option value 0x00010000 = curveEaseIn
option value 0x00020000 = curveEaseOut
option value 0x00030000 = curveLinear
option value 0x00100000 = transitionFlipFromLeft
option value 0x00200000 = transitionFlipFromRight
option value 0x00300000 = transitionCurlUp
option value 0x00400000 = transitionCurlDown
option value 0x00500000 = transitionCrossDissolve
option value 0x00600000 = transitionFlipFromTop
option value 0x00700000 = transitionFlipFromBottom
option value 0x03000000 = preferredFramesPerSecond60
option value 0x07000000 = preferredFramesPerSecond30
The first 7 flags in both sets have the same names and the same values. The 0 value for KeyframeAnimationOptions
is calculationModeLinear
and it's curveEaseInOut
for UIView.AnimationOptions. It results in ease-in/ease-out timing for both keyframe and "regular" UIView animation.
All the other constants have values that are unique in both OptionSets, suggesting some of the other UIView.AnimationOptions
might work for keyframe animation. I've only tested the timing curve values though, and those all work as expected.
Edit #2:
It isn't immediately obvious how the keyframe animation flags (like calculationModeLinear , calculationModeCubic, and calculationModeCubicPaced) and the View animation flags (like curveLinear, curveEaseInOut, etc.) would interact, since the keyframe animation flags also related to animation timing. I wrote a test application to see how they do. It turns out that the UIView.AnimationOptions timing curve flags affect the overall timing of the entire keyframe animation. (The way the overall keyframe animation starts and stops.)
You can download the sample project from github here.
It's a little hard to see ease-in/ease-out UIView animation timing combined with one of the cubic keyframe animation options. It's easier to see the ease-in/ease-out with keyframe calculationModeLinear mode.
Below is a video of that combo:
(Note how the cyan square coasts to a stop in the lower-right corner of the animation rectangle before returning to it's starting point.)

- 128,072
- 22
- 173
- 272
-
This is pretty awesome stuff, thanks for posting this here. It's incredibly helpful! – Tiny Tim Dec 01 '22 at 02:29