我最近发现了一些UIView动画代码并注意到它没有正确使用animateKeyframesWithDuration:delay:options:animations:completion:
方法,并且一直试图修复它,但由于某种原因,动画并不完全相同。
这是我找到的代码:
view.transform = CGAffineTransformMakeTranslation(300, 0);
[UIView animateKeyframesWithDuration:duration/4 delay:delay options:0 animations:^{
// End
view.transform = CGAffineTransformMakeTranslation(-10, 0);
} completion:^(BOOL finished) {
[UIView animateKeyframesWithDuration:duration/4 delay:0 options:0 animations:^{
// End
view.transform = CGAffineTransformMakeTranslation(5, 0);
} completion:^(BOOL finished) {
[UIView animateKeyframesWithDuration:duration/4 delay:0 options:0 animations:^{
// End
view.transform = CGAffineTransformMakeTranslation(-2, 0);
} completion:^(BOOL finished) {
[UIView animateKeyframesWithDuration:duration/4 delay:0 options:0 animations:^{
// End
view.transform = CGAffineTransformMakeTranslation(0, 0);
} completion:^(BOOL finished) {
}];
}];
}];
}];
这有两个问题:
animateKeyframesWithDuration:delay:options:animations:completion:
时,您应该在动画块中调用addKeyframeWithRelativeStartTime:relativeDuration:animations:
方法。如果不调用此方法,则此方法的行为类似于标准的UIView动画块。See Apple's documentation regarding this method here.
所以我尝试通过正确使用该方法来解决这个问题:
view.transform = CGAffineTransformMakeTranslation(300, 0);
double frameDuration = 1.0/4.0; // 4 = number of keyframes
[UIView animateKeyframesWithDuration:duration delay:delay options:0 animations:^{
[UIView addKeyframeWithRelativeStartTime:0*frameDuration relativeDuration:frameDuration animations:^{
view.transform = CGAffineTransformMakeTranslation(-10, 0);
}];
[UIView addKeyframeWithRelativeStartTime:1*frameDuration relativeDuration:frameDuration animations:^{
view.transform = CGAffineTransformMakeTranslation(5, 0);
}];
[UIView addKeyframeWithRelativeStartTime:2*frameDuration relativeDuration:frameDuration animations:^{
view.transform = CGAffineTransformMakeTranslation(-2, 0);
}];
[UIView addKeyframeWithRelativeStartTime:3*frameDuration relativeDuration:frameDuration animations:^{
view.transform = CGAffineTransformMakeTranslation(0, 0);
}];
} completion:nil];
我觉得两个动画应该是相同的,但事实并非如此。在我的尝试中我做错了什么?
编辑:Example project(使用Command + T切换模拟器中的慢动画)
答案 0 :(得分:17)
tl; dr :它们可能看起来不同,因为它们做了不同的事情并导致将不同的动画添加到两个视图中。不幸的是,我真的不知道如何解决它。
正如您已经说过,第一个版本没有正确使用API。它不使用addKeyframeWithRelativeStartTime:relativeDuration:animations:
添加关键帧,而是直接在动画块内更改属性。然后它会在完成块中创建一个新的“关键帧动画”(你很快就会看到我在那里使用了恐怖引号)。
在幕后,这实际上不会导致CAKeyframeAnimations
(尽管我认为你不应该依赖这个事实)。这是我对四个动画对象所做的一些日志记录,它们被添加到项目中view1
后面的图层中。 (我作弊了一下,只记录了变换的.m41
部分(对应于x的平移)。
BASIC
keyPath: transform
duration: 0.125
from: 300.0
model value: -10.0
timing: easeInEaseOut
BASIC
keyPath: transform
duration: 0.125
from: -10.0
model value: 5.0
timing: easeInEaseOut
BASIC
keyPath: transform
duration: 0.125
from: 5.0
model value: -2.0
timing: easeInEaseOut
BASIC
keyPath: transform
duration: 0.125
from: -2.0
model value: 0.0
timing: easeInEaseOut
正如您所看到的,每个动画都有fromValue
但toValue
或byValue
。正如您在文档中看到的那样,这意味着:
fromValue
不是 -nil
。在fromValue
和属性的当前显示值之间进行插值。
另一方面,正确添加关键帧到动画的第二个版本会导致将单个CAKeyframeAnimation添加到view2
后面的层。 (我仍然只记录变换的.m41
部分)
KEYFRAME
keyPath: transform
duration: 0.500
values: (
300,
-10,
5,
-2,
0
)
keyTimes: (
0.0,
0.25,
0.5,
0.75,
1.0
)
timing: easeInEaseOut
timings: (nil)
calculationMode: linear
正如您所看到的,它在相同的值之间以相同的时间动画,但在单个关键帧动画中。它也具有相同的总持续时间并使用相同的计时功能。
有一件事我不理解。如果我在调用super之前修改了真正的关键帧动画(在我被覆盖的addAnimation:forKey:
内部)以在计时函数数组中显式设置计时函数,那么它们看起来完全相同。也就是说,我在关键帧动画添加到图层之前执行此操作。
CAMediaTimingFunction *function = keyFrame.timingFunction;
keyFrame.timingFunction = nil;
keyFrame.timingFunctions = @[function, function, function, function];
这个技巧非常丑陋,除了纯粹的调试之外,你不应该把它用于其他事情!
嗯,我想答案与UIKit在第二种情况下创建的关键帧动画的计时函数数组有关。这几乎是我得到的唯一结论。
那就是说,我不知道这是一个错误还是如何修复它。我只知道你的项目中的代码实际上在核心动画级别上做了什么。