更新模型层时,CABasicAnimation无法正确设置动画 [英] CABasicAnimation does not animate correctly when I update model layer

查看:138
本文介绍了更新模型层时,CABasicAnimation无法正确设置动画的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在实施一个 CABasicAnimation ,它会动画 CALayer 转换财产。
现在,虽然我是Core Animation的新手,但我已经能够收集各种博客和文章,例如objc.io,使用经常(错误的)推荐的方法获取动画是一个非常糟糕的主意坚持使用动画的 fillMode removedOnCompletion 属性。这种方法被很多人认为是不好的做法,因为它会在模型​​层和表示层之间产生差异,因此将来对其中一个层的查询可能与用户看到的不匹配。

I'm currently implementing a CABasicAnimation that animates a CALayer transform property. Now, although I'm new to Core Animation, I have been able to gather through various blogs and articles such as objc.io that it is a very bad idea to use the often (incorrectly) recommended method for getting animations to stick using the fillMode and removedOnCompletion properties of an animation. This method is considered bad practice by many because it creates a discrepancy between the model layer and the presentation layer, and so future queries to one of those layers may not match up with what the user is seeing.

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

Instead, the recommended way of doing animations is to update the model layer at the same time you add the animation to the layer being animated. However, I'm having trouble understanding exactly how this is working. My animation is simple, and goes like this:

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

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

I've denoted three locations where I have attempted to update the model layer using the code

self.layerBeingAnimated.transform = updatedTransform;

在位置1,图层跳转到 newTransform 并且没有动画。
在位置2中,图层完全按照我想要的方式设置动画,从当前变换到 newTransform
在位置3,图层跳转到 newTransform ,跳回旧转换,从fromValue到newTransform正确动画,然后保持 newTransform

In location 1, the layer jumps right to newTransform and does not animate. In location 2, the layer animates exactly as I want it to from the current transform to newTransform. In location 3, the layer jumps right to newTransform, jumps back to the the old transform, animates correctly from the fromValue to newTransform, and then stays at newTransform.

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

What's the deal here? What's the correct location to update the model layer and why are these three locations producing such different results?

谢谢!

推荐答案

我认为最简单的方法是解释三个地点的每一个地方的情况,最后是结论。

I think that it's easiest to explain what is happening for each of the three locations and then a "conclusion" at the end.

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

I'm also adding some illustrations, showing exactly the behaviour that you are mentioning in your question so that it will be easier to follow for someone who hasn't tried these three things themselves. I'm also extending the illustration to show both a stand alone layer and a backing layer (one that is attached to a view) and I will explain the difference where there is one.

在第一个位置,模型值在创建动画之前更新。完成此操作后,transform属性将保存updatedTransform。这意味着当您从fromValue的图层读取变换时,您将获得updatedValue。这反过来意味着往返于值都是相同的,因此您无法看到动画。

In the first location, the model value is updated before the animation is created. Once this is done, the transform property holds the updatedTransform. This means that when you read the transform from the layer for the fromValue, you get the updatedValue back. This in turn means that both to and from value are the same so you can't see the animation.

可能使此位置按预期工作的一件事是在分配之前读取oldValue新值然后使用它作为fromValue。这将按预期显示。

One thing that could have made this location work as expected is to read the oldValue before assigning the new value and then use that as the fromValue. This will look as expected.

// 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上的转换属性是可动画的,当值发生变化时,它将自动执行隐式动画。这意味着动画将添加到变换键路径的图层中。但是,当更改发生在动画块之外时,视图会禁用此行为,因此没有隐式动画。

Location 2

In the second example, the value isn't yet updated when you read the transform for the from value, so the fromValue and toValue are different. After that, the model value is updated to it's final value. There is actually a difference between the stand-alone layer and the backing layer here, but we don't see it. The transform property on CALayer is animatable and will automatically perform an "implicit" animation when the value changes. This means that an animation will be added to the layer for the "transform" key path. The view, however, disables this behaviour when the change happens outside of an animation block, so there is not implicit animation there.

我们之所以看不到隐式动画是随后为相同的键路径添加显式动画。这意味着在两种情况下都会看到唯一的显式动画,甚至认为在独立图层上运行了两个动画(稍后会详细介绍)。如果您感到谨慎,那么您可以禁用独立层的隐式操作(稍后会详细介绍)。

The reason why we don't see the implicit animation is that the "explicit" animation is added afterwards for the same key path. This means that the only explicit animation will be visible, in both cases, even thought there are two animation running on the stand-alone layer (more on that later). If you are feeling cautious, then you could disable the implicit action for the stand-alone layer (more on that later).

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

This leaves us with the last location. In this case the animation is created just as above, with different fromValue and toValue. The only difference is the order of adding the explicit animation and changing the property which triggers an implicit animation. In this case the implicit animation is added after the explicit animation and they both run(!). Both animations actually ran for location 2, but we couldn't see it because the explicit (longer) animation was added before.

由于一切都在快速移动,我将整个图层放慢速度尝试说明当两个动画一次运行时发生了什么。通过这种方式,可以更容易地看到隐式动画结束时会发生什么。我覆盖了表现良好的背衬层和行为不端的独立层,使它们都是50%透明的。虚线轮廓是原始框架。

Since everything is moving so fast, I slowed down the entire layer to try and illustrate what is happening when two animations are running at once. This way it becomes much easier to see what happens when the implicit animation ends. I've overlaid the well behaving backing layer and the misbehaving stand-alone layer and made them both 50% transparent. The dashed outline is the original frame.

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

A short description of what is happening: the blue view get's only the explicit animation added to it (which has a 1 second duration). The orange layer first has the same explicit animation added to it and then has an implicit 0.25 second animation added to it. Neither explicit nor the implicit animations are "additive", meaning their toValue and fromValue are used as-is.

免责声明:我不在Apple和我工作还没有看到核心动画的源代码,所以我要说的是根据行为的方式进行猜测。

Disclaimer: I do not work at Apple and I haven't seen the source code for Core Animation so what I'm about to say is guesswork based on how things behave.

在我的理解中(参见免责声明)这是每次屏幕刷新产生动画时会发生的情况:对于当前时间戳,图层按照添加的顺序浏览动画并更新演示文稿值。在这种情况下,显式动画设置旋转变换,然后隐式动画到来并设置另一个完全覆盖显式变换的旋转变换。

In my understanding (see disclaimer) this is what happens for every screen refresh to produce the animation: for the current time stamp, the layer goes through the animations in the order they were added and updates the presentation values. In this case, the explicit animation sets a rotation transform, then the implicit animation comes and sets another rotation transform that completely overrides the explicit transform.

如果动画配置为添加,它将添加到演示文稿值而不是覆盖(这是超级强大的)。即使使用添加动画,订单仍然很重要。非加法动画可能会在以后出现并覆盖整个事物。

If an animation is configured to be "additive", it will add to the presentation values instead of overwriting (which is super powerful). Even with additive animations, order still matters. A non-additive animation could come later and overwrite the whole thing.

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

Since the implicit animation is shorter than the explicit one, we see that for the first part of the total animation, the values are strictly coming from the implicit animation (which was added last). Once the implicit animation finishes, the only remaining animation is the explicit animation which has been running underneath the implicit one, all this time. So when the implicit animation finishes, the explicit animation has already progressed 0.25 seconds and we see that the orange layer jumps back to the same value as the blue view, instead of jumping back to the beginning.

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

At this point, the question is, how can we prevent two animations from being added and where should we update the value? The location where the value is updated doesn't prevent there from being two animations (but it can affect how the end result looks).

防止添加两个动作对于独立层,我们暂时禁用所有操作(动画的更通用术语):

To prevent two actions from being added to the stand-alone layer, we temporarily disable all "actions" (a more general term for an animation):

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

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

When we do this, only one animation is added to the layer so either location 2 or 3 works. That is simply a matter of taste. If you read the oldValue, the you can also use location 1 (as long as the action is disabled).

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

If you are animating a backing layer then you don't have to disable actions (the view does that for you) but it also doesn't hurt to do so.

此时我可以继续介绍配置动画的其他方法,添加动画是什么,为什么你在这种情况下需要指定toValue和fromValue。但我认为我已经回答了你提出的问题,而这个答案已经有点偏僻。

At this point I could keep going about other ways to configure an animation, what an additive animation is, and why you needed to specify both the toValue and fromValue in this case. But I think that I have answered the question you asked and that this answer already is a bit on the long side.

这篇关于更新模型层时,CABasicAnimation无法正确设置动画的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆