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

iMa*_*din 9 core-animation objective-c uiview uiviewanimation ios7

我最近发现了一些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) {
            }];
        }];
    }];
}];
Run Code Online (Sandbox Code Playgroud)

这有两个问题:

  1. 应该使用关键帧动画而不是在完成块内嵌套动画.
  2. 使用时animateKeyframesWithDuration:delay:options:animations:completion:你应该调用addKeyframeWithRelativeStartTime:relativeDuration:animations:动画块内的方法.如果不调用此方法,则此方法的行为类似于标准UIView动画块.

请参阅Apple有关此方法的文档.

所以我尝试通过正确使用该方法来解决这个问题:

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];
Run Code Online (Sandbox Code Playgroud)

我觉得两个动画应该是相同的,但它们不是.在我的尝试中我做错了什么?

编辑:示例项目(使用Command + T切换模拟器中的慢动画)

Dav*_*ist 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
Run Code Online (Sandbox Code Playgroud)

正如你可以看到每个动画有fromValue,但没有toValuebyValue.正如您在文档中看到的那样,这意味着:

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
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,它在相同的值之间以相同的时间动画,但在单个关键帧动画中.它也具有相同的总持续时间并使用相同的计时功能.

有一件事我不理解.如果我修改真正的关键帧动画(addAnimation:forKey:在调用super之前重写的内部)以在计时函数数组中显式设置计时函数,那么它们看起来完全相同.也就是说,我在关键帧动画添加到图层之前执行此操作.

CAMediaTimingFunction *function = keyFrame.timingFunction;
keyFrame.timingFunction = nil;
keyFrame.timingFunctions = @[function, function, function, function]; 
Run Code Online (Sandbox Code Playgroud)

这个技巧非常丑陋,除了纯粹的调试之外你永远不应该使用它!


那么,为什么它们看起来不同?

好吧,我想答案与UIKit在第二种情况下创建的关键帧动画的计时函数数组有关.这几乎是我得到的唯一结论.

也就是说,我不知道这是一个错误还是如何解决它.我只知道你的项目中的代码实际上在Core Animation级别上做了什么.

  • UIKit实际上将`timingFunctions`留空了.它设置`timingFunction`就是这样.在UIKit方面没有API(从iOS 9开始).现在,我创建了这个小扩展,允许在事后调整`timingFunction`和`timingFunctions`:https://github.com/salutis/View-KeyframeAnimationCurve (3认同)