简体   繁体   English

使用CABasicAnimation多次旋转UIImageView

[英]Using CABasicAnimation to rotate a UIImageView more than once

I am using a CABasicAnimation to rotate a UIImageView 90 degrees clockwise, but I need to have it rotate a further 90 degrees later on from its position after the initial 90 degree rotation. 我正在使用CABasicAnimation将UIImageView顺时针旋转90度,但是我需要让它在初始旋转90度后从其位置再旋转90度。

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
animation.duration = 10;
animation.additive = YES;
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeForwards;
animation.fromValue = [NSNumber numberWithFloat:DEGREES_TO_RADIANS(0)];
animation.toValue = [NSNumber numberWithFloat:DEGREES_TO_RADIANS(90)];
[_myview.layer addAnimation:animation forKey:@"90rotation"];

Using the code above works initially, the image stays at a 90 degree angle. 使用上面的代码最初可以工作,图像保持90度角。 If I call this again to make it rotate a further 90 degrees the animation starts by jumping back to 0 and rotating 90 degrees, not 90 to 180 degrees. 如果我再次调用它使其旋转90度,则动画将从跳回0并旋转90度而不是90至180度开始。

I was under the impression that animation.additive = YES; 我的印象是animation.additive = YES; would cause further animations to use the current state as a starting point. 会导致其他动画将当前状态用作起点。

Any ideas? 有任何想法吗?

tl;dr: It is very easy to misuse removeOnCompletion = NO and most people don't realize the consequences of doing so. tl; dr:很容易滥用removeOnCompletion = NO并且大多数人没有意识到这样做的后果。 The proper solution is to change the model value "for real". 适当的解决方案是“实际”更改模型值。


First of all: I'm not trying to judge or be mean to you. 首先:我不是要判断你或对你卑鄙。 I see the same misunderstanding over and over and I can see why it happens. 我一遍又一遍地看到同样的误解,我明白了为什么会发生。 By explaining why things happen I hope that everyone who experience the same issues and sees this answer learn more about what their code is doing. 通过解释为什么会发生这种情况,我希望遇到同样问题并看到此答案的每个人都了解他们的代码在做什么。

What went wrong 什么地方出了错

I was under the impression that animation.additive = YES; 我的印象是animation.additive = YES; would cause further animations to use the current state as a starting point. 会导致其他动画将当前状态用作起点。

That is very true and it's exactly what happens. 这是真的,这正是发生的情况。 Computers are funny in that sense. 从这个意义上说,计算机很有趣。 They always to exactly what you tell them and not what you want them to do. 他们总是完全按照您告诉他们的内容,而不是您想要他们做什么。

removeOnCompletion = NO can be a bitch removeOnCompletion = NO可能是个bit子

In your case the villain is this line of code: 在您的情况下,反派是这一行代码:

animation.removedOnCompletion = NO;

It is often misused to keep the final value of the animation after the animation completes. 动画完成后,经常会滥用它来保留动画的最终值。 The only problem is that it happens by not removing the animation from the view. 唯一的问题是,它是通过不从视图中删除动画来发生的。 Animations in Core Animation doesn't alter the underlying property that they are animating, they just animate it on screen. “核心动画”中的动画不会更改其设置动画的基础属性,而只是在屏幕上对其进行动画处理。 If you look at the actual value during the animation you will never see it change. 如果在动画过程中查看实际值,您将永远看不到它的变化。 Instead the animation works on what is called the presentation layer. 取而代之的是,动画在所谓的表示层上工作。

Normally when the animation completes it is removed from the layer and the presentation layer goes away and the model layer appears on screen again. 通常,动画完成后,将其从图层中删除,表示层消失,模型层再次出现在屏幕上。 However, when you keep the animation attached to the layer everything looks as it should on screen but you have introduced a difference between what the property says is the transform and how the layer appears to be rotated on screen. 但是,当您将动画附加到图层时,所有内容在屏幕上看起来都应该看起来一样,但是您引入了属性所说的变换与图层在屏幕上看起来如何旋转之间的区别。

When you configure the animation to be additive that means that the from and to values are added to the existing value, just as you said. 正如您所说,将动画配置为可叠加时,意味着将from和to值添加到现有值中。 The problem is that the value of that property is 0. You never change it, you just animate it. 问题在于该属性的值为0。您永远不会对其进行更改,而只是对其进行动画处理。 The next time you try and add that animation to the same layer the value still won't be changed but the animation is doing exactly what it was configured to do: "animate additively from the current value of the model ". 下次尝试将该动画添加到同一层时,该值仍不会更改,但该动画将完全按照其配置进行操作:“从模型的当前值进行附加动画”。

The solution 解决方案

Skip that line of code. 跳过那行代码。 The result is however that the rotation doesn't stick. 但是结果是旋转不固定。 The better way to make it stick is to change the model. 使其坚持下去的更好方法是更改​​模型。 Set the new end value of the rotation before animating the rotation so that the model looks as it should when the animation gets removed. 在对旋转进行动画处理之前,设置旋转的新的最终值,以使模型在删除动画时看起来像应该的样子。

byValue is like magic byValue就像魔术

There is a very handy property (that I'm going to use) on CABasicAnimation that is called byValue that can be used to make relative animations. CABasicAnimation上有一个非常方便的属性(我将要使用),该属性称为byValue ,可用于制作相对动画。 It can be combined with either toValue and fromValue to do many different kinds of animations. 它可以结合两种toValuefromValue做许多不同种类的动画。 The different combinations are all specified in its documentation (under the section). 不同的组合都在其文档中指定了(在本节下面)。 The combination I'm going to use is: 我要使用的组合是:

byValue and toValue are non- nil . byValuetoValue是非nil Interpolates between (toValue - byValue) and toValue . 之间进行插值(toValue - byValue)toValue

Some actual code 一些实际的代码

With an explicit toValue of 0 the animation happens from "currentValue-byValue" to "current value". 显式的toValue为0时,动画发生在“ currentValue-byValue”到“ current value”之间。 By changing the model first current value is the end value. 通过更改模型,第一个当前值是最终值。

NSString *zRotationKeyPath = @"transform.rotation.z"; // The killer of typos

// Change the model to the new "end value" (key path can work like this but properties don't)
CGFloat currentAngle = [[_myview.layer valueForKeyPath:zRotationKeyPath] floatValue];
CGFloat angleToAdd   = M_PI_2; // 90 deg = pi/2
[_myview.layer setValue:@(currentAngle+angleToAdd) forKeyPath:zRotationKeyPath]; 

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:zRotationKeyPath];
animation.duration = 10;
// @( ) is fancy NSNumber literal syntax ...
animation.toValue = @(0.0);        // model value was already changed. End at that value
animation.byValue = @(angleToAdd); // start from - this value (it's toValue - byValue (see above))

// Add the animation. Once it completed it will be removed and you will see the value
// of the model layer which happens to be the same value as the animation stopped at.
[_myview.layer addAnimation:animation forKey:@"90rotation"];

Small disclaimer : 小免责声明

I didn't run this code but am fairly certain that it runs as it should and that I didn't do any typos. 我没有运行此代码,但可以肯定的是它可以按预期运行,并且我没有做任何错别字。 Correct me if I did. 如果我纠正了我。 The entire discussion is still valid. 整个讨论仍然有效。

pass incremental value of angle see my code 通过角度增量值请参阅我的代码

static int imgAngle=0;
- (void)doAnimation
{
  CABasicAnimation *animation = [CABasicAnimation   animationWithKeyPath:@"transform.rotation.z"];
   animation.duration = 5;
  animation.additive = YES;
  animation.removedOnCompletion = NO;
  animation.fillMode = kCAFillModeForwards;
  animation.fromValue = [NSNumber numberWithFloat:DEGREES_TO_RADIANS(imgAngle)];
  animation.toValue = [NSNumber numberWithFloat:DEGREES_TO_RADIANS(imgAngle+90)];
 [self.imgView.layer addAnimation:animation forKey:@"90rotation"];

  imgAngle+=90;
  if (imgAngle>360) {
      imgAngle = 0;
  }
}

Above code is just for idea. 上面的代码仅供参考。 Its not tested 未经测试

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM