当我更新模型图层时,CABasicAnimation不能正确animation

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

相反,推荐的animation制作方法是在将animation添加到正在animation的图层的同时更新模型图层。 但是,我很难理解这是如何工作的。 我的animation很简单,像这样:

 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并且不animation。 在位置2中,该层完全按照我希望从当前转换到newTransformanimation。 在位置3,图层跳转到newTransform ,跳回到旧的转换,从fromValue正确地animation到newTransform,然后停留在newTransform

这里的交易是什么? 什么是更新模型图层的正确位置,以及为什么这三个位置会产生不同的结果?

谢谢!

我认为最简单的解释三个地点的每一个正在发生的事情,然后在最后一个“结论”。

我还添加了一些插图,准确地显示了你在问题中提到的行为,这样对于没有尝试过这三件事情的人来说更容易。 我还扩展插图以显示一个独立层和一个支持层(一个附加到视图),我将解释有什么区别。

地点1

在第一个位置,模型值在animation创build之前被更新。 一旦完成,transform属性将保存updatedTransform。 这意味着当你读取fromValue图层的变换时,你会得到updatedValue。 这反过来又意味着价值和价值是相同的,所以你看不到animation。

在这里输入图像说明

有一件事可以使这个位置按预期工作,在分配新值之前读取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是不同的。 之后,模型值更新为最终值。 这里的独立层和背景层实际上是有区别的,但是我们没有看到它。 CALayer上的transform属性是animation的,当值改变时会自动执行“隐式”animation。 这意味着animation将被添加到“变换”键path的图层中。 然而,该视图在animation块之外发生更改时会禁用此行为,因此不存在隐式animation。

我们看不到隐式animation的原因是之后为相同的键path添加了“显式”animation。 这意味着在这两种情况下,只有显式的animation才会被看到,甚至认为在独立层上有两个animation运行(稍后会更多)。 如果你觉得谨慎,那么你可以禁用独立层的隐式动作(稍后会介绍)。

在这里输入图像说明

地点3

这留给我们最后的位置。 在这种情况下,animation就像上面一样创build,不同于从值和值。 唯一的区别是添加显式animation和更改触发隐式animation的属性的顺序。 在这种情况下,隐式animation是在显式animation之后添加的,它们都运行(!)。 这两个animation实际上都是为位置2运行的,但我们无法看到它,因为显式(较长)的animation是在后面添加的。

在这里输入图像说明

由于一切都是如此之快,我放慢了整个层面,试图说明当两个animation同时运行时发生的事情。 这样,隐式animation结束后会发生什么变得更容易。 我已经覆盖了良好的行为背衬层和行为不端的独立层,使他们都50%透明。 虚线轮廓是原始的框架。

在这里输入图像说明

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

免责声明:我不在苹果公司工作,我还没有看到核心animation的源代码,所以我要说的是基于事情的performance猜测。

在我的理解(请参阅免责声明),这是每个屏幕刷新产生的animation会发生什么:对于当前的时间戳,图层按照他们添加的顺序通过animation,并更新演示值。 在这种情况下,显式animation设置旋转变换,然后隐式animation出现,并设置另一个旋转变换,完全覆盖显式变换。

如果一个animation被configuration为“可叠加”,它将添加到演示文稿值而不是覆盖(这是超级强大的)。 即使有添加animation,顺序仍然很重要。 一个非加性animation可能会晚一点,覆盖整个事情。

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

我们应该在哪里更新价值?

在这一点上,问题是,我们如何防止添加两个animation,我们应该在哪里更新值? 值的更新位置并不妨碍它有两个animation(但会影响最终结果的外观)。

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

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

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

如果你是animation背景层,那么你不必禁用动作(视图为你做),但也不伤害这样做。


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