我目前正在实施动态CABasicAnimation
CALayer
属性的transform
。
现在,虽然我是Core Animation的新手,但我已经能够收集各种博客和文章,例如objc.io,使用经常(错误的)推荐的方法来制作动画是一个非常糟糕的主意使用动画的fillMode
和removedOnCompletion
属性。许多人认为这种方法很糟糕,因为它会在模型层和表示层之间产生差异,因此将来对其中一个图层的查询可能与用户看到的不匹配。
相反,推荐的动画制作方法是在将动画添加到动画图层的同时更新模型图层。但是,我很难理解这是如何工作的。我的动画很简单,就像这样:
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
。
这里的交易是什么?更新模型图层的正确位置是什么?为什么这三个位置会产生不同的结果?
谢谢!
答案 0 :(得分:73)
我认为最容易解释三个地点的每一个地方发生了什么,然后是一个"结论"最后。
我还添加了一些插图,准确显示了您在问题中提到的行为,以便对那些自己没有尝试过这三件事的人更容易理解。我还扩展了插图,展示了一个独立层和一个背衬层(一个附着在视图上),我将解释其中有一个区别。
在第一个位置,模型值在创建动画之前更新。完成此操作后,transform属性将保存updatedTransform。这意味着当您从fromValue的图层读取变换时,您将获得updatedValue。这反过来意味着往返于值和从值都是相同的,因此您无法看到动画。
可能使此位置按预期工作的一件事是在分配新值之前读取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];
在第二个示例中,当您读取from值的变换时,值尚未更新,因此fromValue和toValue不同。之后,模型值将更新为其最终值。这里的独立层和背衬层实际上有区别,但我们没有看到它。 CALayer上的transform
属性是可动画的,并且会自动执行"隐式"值改变时的动画。这意味着将动画添加到图层中以进行"转换"关键路径。但是,当更改发生在动画块之外时,视图会禁用此行为,因此没有隐式动画。
我们没有看到隐式动画的原因是"显式"之后为相同的键路径添加动画。这意味着在两种情况下都会显示唯一的显式动画,甚至认为在独立图层上运行了两个动画(稍后会详细介绍)。如果您感到谨慎,那么您可以禁用独立层的隐式操作(稍后会详细介绍)。
这给我们留下了最后的位置。在这种情况下,动画就像上面一样创建,具有不同的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的原因。但我认为我已经回答了你提出的问题,这个答案已经有点偏僻。