I've just created a keyframe animation like this:
[UIView animateKeyframesWithDuration:10 delay:0 options:0 animations:^{
[UIView addKeyframeWithRelativeStartTime:0 relativeDuration:.1 animations:^{
view.alpha = 0;
}];
} completion:nil];
And this is a CAKeyframeAnimation
that gets created:
(lldb) po [self.layer animationForKey:@"opacity"]
<CAKeyframeAnimation:0x10a6364b0; keyTimes = (
0,
"0.1",
1
); values = (
1,
0,
0
); calculationMode = linear; delegate = <UIViewKeyframeAnimationState: 0x10a6358d0>; fillMode = both; timingFunction = easeInEaseOut; duration = 10; keyPath = opacity>
Question:
The entire animation should take 10 seconds with the opacity animating for 10 * 0.1 = 1 second, right? When I look at the animation, the change is being animated way longer than 1 second.
Why?
The reason is that the animation's timing function is not linear.
Layer and animations use a hierarchical timing system, where each object has its own local time which is dependant of its parents and its own timing parameters.
For instance, animations have a timing function that defines their pacing. The default timing functions for an animation created by UIKit is the ease-in ease-out timing function which defines a timing that begins slowly, accelerates through the middle of its duration, and then slows again before completing. It makes for smooth animations that don't start or stop abruptly. However it will also temper with the key times of your keyframe animation which is annoying when you need precise timing.
You can disable the ease-in ease-out timing by passing the
UIViewAnimationOptionCurveLinear
option to theanimateKeyframesWithDuration:delay:options:animations:completion:
method. I'm not sure whether it's intended as you need to cast the value fromUIViewAnimationOptions
toUIViewKeyframeAnimationOptions
and the documentation does not say anything about that. Maybe a better (but more verbose) way is to embed the keyframe animation block inside a standard animation block like this: