这是我发现的代码:
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:动画块中的动画:方法.如果你不调用这个方法,那么这个方法的行为就像一个标准的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(使用命令T来切换模拟器中的慢速动画)
解决方法
第一版后面发生了什么
正如您已经说过的第一个版本不正确使用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
is non-nil
. Interpolates betweenfromValue
and the current presentation value of the property.
第二版后面发生了什么
另一方面,向动画正确添加关键帧的第二个版本会导致将此单个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
正如你可以看到,它在相同的值之间在相同的时间,但在一个关键帧动画动画.它也具有相同的总持续时间并使用相同的定时功能.
有一件事我不明白.如果我修改真正的关键帧动画(在我重写的addAnimation内部:forKey:在调用super之前)显式地设置定时功能阵列中的定时功能,那么它们看起来完全相同.也就是说,我在关键帧动画添加到图层之前做到这一点.
CAMediaTimingFunction *function = keyFrame.timingFunction; keyFrame.timingFunction = nil; keyFrame.timingFunctions = @[function,function,function];
这个伎俩是非常丑陋的,你绝对不应该使用它,除了纯粹的调试之外的事情!
那为什么他们看起来不一样?
那么,我想这个答案与UIKit在第二种情况下创建的关键帧动画的时序函数数组有关.这几乎是我得到的唯一结论.
也就是说,我不知道这是一个bug还是修复它.我只是知道您的项目中的代码实际上在核心动画级别.