CABasicAnimation 从当前图层位置开始

Posted

技术标签:

【中文标题】CABasicAnimation 从当前图层位置开始【英文标题】:CABasicAnimation start from current layer position 【发布时间】:2014-09-28 05:56:33 【问题描述】:

这是我进行 Obj-C 编程的第二周,我在动画方面遇到了一点问题。

我用这个动画:

 CABasicAnimation *fullRotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
    fullRotation.toValue = [NSNumber numberWithFloat:((360*M_PI)/180)];
    fullRotation.duration = 4;
    fullRotation.repeatCount= 1000;
    [[stick layer] addAnimation:fullRotation forKey:@"60"];

此动画在我的应用程序启动时开始,然后我单击一些按钮,这些按钮在单击时会更改动画持续时间,但新动画(具有相同的代码但持续时间不同)从图像的原始位置开始“戳”。我该怎么做才能使其他动画从正在进行 360 度转动的摇杆的当前位置开始?谢谢。

部分代码解释:

-(void)viewDidAppear:(BOOL)animated
    CABasicAnimation *fullRotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
    fullRotation.toValue = [NSNumber numberWithFloat:((360*M_PI)/180)];
    fullRotation.duration = 4;
    fullRotation.repeatCount= 1000;
    [[stick layer] addAnimation:fullRotation forKey:@"60"];

- (IBAction)button1:(UIButton *)sender 

 CABasicAnimation *fullRotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
    fullRotation.toValue = [NSNumber numberWithFloat:((360*M_PI)/180)];
    fullRotation.duration = 6;
    fullRotation.repeatCount= 1000;
    [[stick layer] addAnimation:fullRotation forKey:@"60"];

【问题讨论】:

你知道模型值和表示值的区别吗? @DavidRönnqvist 不幸的是,我不是。就像我说的,我刚刚开始尝试。如果我没有达到您的期望,我真的很抱歉。问候。 【参考方案1】:

有几种不同的方法可以实现您所追求的结果,但在查看这些方法之前,我应该解释问题的根本原因。

发生了什么

您在动画期间在屏幕上看到的内容不一定与这些图层上的属性值相匹配。实际上,给图层添加动画并不会改变图层的动画属性。您在屏幕上看到的动画和值发生在渲染服务器中,该服务器在您的应用程序之外的另一个进程中运行。您无法得到这些精确值,但可以得到一个近似值,称为 presentation 值。由于我们无法获取渲染服务器的值,因此我们通常只讨论模型值(图层对象上的实际值)和表示值(屏幕上显示的内容(或至少非常接近的近似值) )。

只为 CABasicAnimation 指定 toValue 意味着它从当前模型值和指定的值开始动画。请注意,文档说它是当前的表示值,但 that is actually incorrect。由于模型值永远不会改变,这意味着当添加第二个动画时,它会从不变的、未旋转的模型值动画到toValue

(附带说明:由于两个动画使用相同的键,新的替换旧的。这并不重要,因为动画不是相加的,所以即使没有替换动画,新的动画会将其值写入旧动画值)。

不同的修复方法

有很多方法可以获得你想要的行为,从最简单的版本开始。

添加显式fromValue

如上所述,只有一个非零toValue,动画将从当前模型值开始。但是,您可以添加一个非零fromValue,它是当前表示值。

您可以从图层的presentationLayer 获取表示值。从中,您可以使用与动画相同的键路径和一点 KVC(键值编码)来获取当前的旋转角度:

NSNumber *currentAngle = [switch.layer.presentationLayer valueForKeyPath:@"transform.rotation"];

如果您只将其设置为动画的fromValue,您将获得正确的起始值,但旋转可能不再是 360 度。虽然您可以计算出角度并创建一个新的toValue,但还有另一个名为byValue 的属性会产生相对变化。指定byValuefromValue(但不指定toValue)意味着:

fromValue(fromValue + byValue) 之间进行插值。

因此,您可以将动画代码更改为:

CABasicAnimation *fullRotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
fullRotation.fromValue = currentAngle; // the value read from the presentation layer
fullRotation.byValue   = @(2.0*M_PI);
fullRotation.duration  = 6.0;

此时您的代码应该从正确的值继续,但动画的整体速度可能看起来有点奇怪(即使在此代码更改之前它也应该看起来很奇怪)。对于连续旋转的视图,您可能希望它始终以相同的速度旋转(而不是每次旋转都加速和减速)。您可以通过将动画配置为具有线性计时功能来获得此功能:

fullRotation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];

这应该会给你你所追求的行为。一些风格上的注释是指定 INFINITYHUGE_VALF 为重复计数,除非您希望它恰好旋转 1000 次,并在添加图层时使用更具描述性的键(除非您将键用于其他用途(比如从图层中移除动画):

fullRotation.repeatCount    = INFINITY;
[stick.layer addAnimation:fullRotation forKey:@"rotate continuously"];

改变图层的速度

在将其用作生产代码中的解决方案之前,我会三思而后行,但它可以作为学习练习很有趣。由于您主要做的是通过将持续时间从 4 秒更改为 6 秒来减慢动画速度,因此您可以做的一件事来创建相同的效果是减慢图层速度。请注意,这会产生副作用(此层上的所有动画及其所有子层也会受到影响)。

一旦将动画添加到图层中,您将无法对其进行修改,但图层本身也符合CAMediaTiming 协议,这意味着它具有speed 属性。将速度设置为4.0/6.0 并将旧动画保留在图层上会减慢速度,使每次旋转需要 6 秒而不是 4 秒。

再一次,作为一个学习练习有点 hacky 但很有趣。

【讨论】:

写得好详细的答案,感谢您的时间和额外的信息。

以上是关于CABasicAnimation 从当前图层位置开始的主要内容,如果未能解决你的问题,请参考以下文章

在 CABasicAnimation 中闪烁以进行旋转

在 CABasicAnimation 期间 UIButton 未显示选定状态

使用 CABasicAnimation 后防止 CAGradientLayer 位置返回原始值

当 CABasicAnimation 正在制作动画时,我该如何回调?

CABasicAnimation基础核心动画

重新启动 CABasicAnimation