UIView animateKeyframesWithDuration与标准动画块动画不同

时间:2014-03-12 05:33:13

标签: objective-c uiview ios7 core-animation uiviewanimation

我最近发现了一些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) {
            }];
        }];
    }];
}];

这有两个问题:

  1. 应该使用关键帧动画而不是在完成块内嵌套动画。
  2. 使用animateKeyframesWithDuration:delay:options:animations:completion:时,您应该在动画块中调用addKeyframeWithRelativeStartTime:relativeDuration:animations:方法。如果不调用此方法,则此方法的行为类似于标准的UIView动画块。
  3. 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切换模拟器中的慢动画)

1 个答案:

答案 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

正如您所看到的,每个动画都有fromValuetoValuebyValue。正如您在文档中看到的那样,这意味着:

  

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在第二种情况下创建的关键帧动画的计时函数数组有关。这几乎是我得到的唯一结论。

那就是说,我不知道这是一个错误还是如何修复它。我只知道你的项目中的代码实际上在核心动画级别上做了什么。