更新模型层时,CABasicAnimation 无法正确设置动画

Posted

技术标签:

【中文标题】更新模型层时,CABasicAnimation 无法正确设置动画【英文标题】:CABasicAnimation does not animate correctly when I update model layer 【发布时间】:2014-07-19 07:35:38 【问题描述】:

我目前正在实现一个 CABasicAnimation 动画 CALayer transform 属性。 现在,虽然我是 Core Animation 的新手,但我已经能够通过各种博客和文章(例如 objc.io)收集到,使用经常(错误地)推荐的方法来使用fillModeremovedOnCompletion 动画的属性。这种方法被许多人认为是不好的做法,因为它会在模型​​层和表示层之间产生差异,因此未来对其中一个层的查询可能与用户所看到的不匹配。

相反,推荐的制作动画的方法是在将动画添加到动画层的同时更新模型层。但是,我无法准确理解这是如何工作的。我的动画很简单,如下所示:

CATransform3D updatedTransform = [self newTransformWithCurrentTransform];
// Location 1
CABasicAnimation *transformAnimation = [CABasicAnimation animationWithKeyPath:@"transform"];
transformAnimation.duration = 1;
transformAnimation.fromValue = [NSValue valueWithCATransform3D:self.layerBeingAnimated.transform]; // Does not work without this.
transformAnimation.toValue = [NSValue valueWithCATransform3D:updatedTransform];
// Location 2
[self.layerBeingAnimated addAnimation:transformAnimation forKey:kTransformAnimationKey];
// Location 3

我已经指出了三个我尝试使用代码更新模型层的位置

self.layerBeingAnimated.transform = updatedTransform;

在位置 1,图层直接跳转到 newTransform 并且没有动画。 在位置 2,图层的动画效果完全符合我的要求,从当前变换到 newTransform。 在位置 3,图层直接跳到 newTransform,跳回旧变换,正确地从 fromValue 动画到 newTransform,然后停留在 newTransform

这里有什么问题?更新模型层的正确位置是什么?为什么这三个位置会产生如此不同的结果?

谢谢!

【问题讨论】:

我喜欢你们都完成了阅读,自己进行了实验,并写了一个明确的问题! 【参考方案1】:

我认为最简单的方法是解释这三个地点中的每一个发生了什么,然后在最后给出一个“结论”。

我还添加了一些插图,准确地展示了您在问题中提到的行为,以便那些自己没有尝试过这三件事的人更容易理解。我还将插图扩展为显示独立层和支持层(附加到视图的层),我将解释两者之间的区别。

位置 1

在第一个位置,模型值在动画创建之前更新。完成此操作后,transform 属性将保存 updatedTransform。这意味着,当您从层中读取 fromValue 的转换时,您将返回 updatedValue。这反过来意味着 to 和 from 值相同,因此您看不到动画。

可以使该位置按预期工作的一件事是在分配新值之前读取 oldValue,然后将其用作 fromValue。这将符合预期。

// Location 1
CATransform3D oldValue = layer.transform; // read the old value first
layer.transform = updatedTransform;       // then update to the new value
    
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"transform"];
anim.duration  = 1.0;
anim.fromValue = [NSValue valueWithCATransform3D:oldValue];
anim.toValue   = [NSValue valueWithCATransform3D:updatedTransform];

位置 2

在第二个示例中,当您读取 from 值的转换时,该值尚未更新,因此 fromValue 和 toValue 不同。之后,模型值更新为其最终值。这里的stand-alone layer和backing layer其实是有区别的,但是我们看不到。 CALayer 上的transform 属性是可动画的,并且会在值更改时自动执行“隐式”动画。这意味着将为“变换”关键路径向图层添加动画。但是,当更改发生在动画块之外时,视图会禁用此行为,因此那里没有隐式动画。

我们看不到隐式动画的原因是“显式”动画是在相同的关键路径之后添加的。这意味着在这两种情况下,唯一的显式动画都是可见的,即使有两个动画在独立层上运行(稍后会详细介绍)。如果您感到谨慎,则可以禁用独立层的隐式操作(稍后会详细介绍)。

位置 3

这给我们留下了最后一个位置。在这种情况下,动画就像上面一样创建,但 fromValue 和 toValue 不同。唯一的区别是添加显式动画和更改触发隐式动画的属性的顺序。在这种情况下,隐式动画是在显式动画之后添加的,并且它们都运行(!)。两个动画实际上都是针对位置 2 运行的,但我们看不到它,因为之前添加了显式(更长的)动画。

由于一切都进展得如此之快,我放慢了整个图层的速度,以尝试说明当两个动画同时运行时发生的情况。这样,当隐式动画结束时会更容易看到会发生什么。我已经覆盖了行为良好的背衬层和行为不端的独立层,并使它们都具有 50% 的透明性。虚线轮廓是原始框架。

对正在发生的事情的简短描述:蓝色视图只是添加到它的显式动画(持续时间为 1 秒)。橙色层首先添加了相同的显式动画,然后添加了一个 0.25 秒的隐式动画。显式和隐式动画都不是“加法”,这意味着它们的 toValue 和 fromValue 都按原样使用。

免责声明:我不在 Apple 工作,也没有看过 Core Animation 的源代码,所以我要说的是基于事物行为的猜测。

据我了解(请参阅免责声明),每次屏幕刷新都会产生动画效果:对于当前时间戳,图层按照动画的添加顺序遍历动画并更新表示值。在这种情况下,显式动画设置一个旋转变换,然后隐式动画来设置另一个完全覆盖显式变换的旋转变换。

如果动画被配置为“添加”,它将添加到表示值而不是覆盖(这是超级强大的)。即使使用附加动画,顺序仍然很重要。一个非附加动画可能会在稍后出现并覆盖整个内容。

由于隐式动画比显式动画短,我们看到对于整个动画的第一部分,值严格来自隐式动画(最后添加)。一旦隐式动画完成,唯一剩下的动画就是一直在隐式动画下面运行的显式动画。所以当隐式动画结束时,显式动画已经进行了 0.25 秒,我们看到橙色层跳回了与蓝色视图相同的值,而不是跳回到开头。

我们应该在哪里更新值?

此时的问题是,我们如何防止两个动画被添加,我们应该在哪里更新值?更新值的位置不会阻止出现两个动画(但它会影响最终结果的外观)。

为了防止两个动作被添加到独立层,我们暂时禁用所有“动作”(动画的更通用术语):

[CATransaction begin];
[CATransaction setDisableActions:YES]; // actions are disabled for now
layer.transform = updatedTransform;
[CATransaction commit];                // until here

当我们这样做时,只有一个动画被添加到图层中,所以位置 2 或 3 都可以工作。这只是一个品味问题。如果您阅读 oldValue,您也可以使用位置 1(只要该操作被禁用)。

如果您正在为支持层设置动画,那么您不必禁用操作(视图会为您执行此操作),但这样做也没有什么坏处。


在这一点上,我可以继续讨论其他配置动画的方法,什么是附加动画,以及为什么在这种情况下需要同时指定 toValue 和 fromValue。但我认为我已经回答了你提出的问题,而且这个答案已经有点冗长了。

【讨论】:

哇。你太棒了!当我看到你回答我的问题时,我真的笑了,因为在过去的一周里我两次提到你的博客。 (在阅读您关于使用单位矩形外的锚点旋转 CALayers 的博客之前,我正在做一堆像假人一样的三角操作)另外,我不敢相信我没有意识到我正在丢弃位置 1 中的旧变换在我发布这个之前。那是愚蠢的,对不起。无论如何,这个答案真的很棒,进一步表明您不仅知道自己在做什么,而且可以清楚地向他人解释。非常感谢,希望新人能看到这个 对于那些寻找更多信息的人,作者写了一个blog post关于这个主题。 那篇博文现在是here。

以上是关于更新模型层时,CABasicAnimation 无法正确设置动画的主要内容,如果未能解决你的问题,请参考以下文章

Swift - CABasicAnimation 完成时更新 UIButton

如何在 CABasicAnimation 结束之前重置它?

CABasicAnimation 在动画完成后重置为初始值

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

用示例学习 Keras

ValueError:添加 Keras 层时带有 ast.literal_eval() 的节点或字符串格式错误