使用CABasicAnimation使UIImageView旋转多次

13

我正在使用CABasicAnimation来使一个UIImageView顺时针旋转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"];

使用上述代码最初有效,图像保持在90度角。 如果我再次调用它使其旋转另外90度,则动画将从0开始向后跳并旋转90度,而不是从90到180度。

我认为 animation.additive = YES; 会导致后续动画使用当前状态作为起点。

有任何想法吗?


1
为什么不使用180而不是90作为“toValue”? - Levi
此外,不要将 fromValue 设置为零。 - isaac
@Levi 因为我想先旋转90度再后面再旋转180度,所以需要进行两次90度旋转,而不是一次180度的旋转。 - BytesGuy
@isaac 尝试将其移除,但似乎没有任何区别。 - BytesGuy
在添加动画时,指定一个良好的键名而不是使用属性名称,这样可以获得+1分。 - David Rönnqvist
2个回答

67

tl;dr: 非常容易误用removeOnCompletion = NO,大多数人没有意识到这样做的后果。正确的解决方法是真正更改模型值。


首先,我并不想评判或针对你。我一遍又一遍地看到同样的误解,我也理解为什么会发生这种情况。通过解释“为什么”事情会发生,我希望每个经历过同样问题并看到这篇答案的人能够更多地了解他们的代码正在做什么。

出了什么问题

我认为animation.additive = YES;会导致进一步的动画使用当前状态作为起点。

那非常正确,这正是发生的事情。在这方面,电脑很有趣。它们总是按照你所“告诉”的方式执行,而不是按照你所“期望”的方式执行。

removeOnCompletion = NO可能会造成麻烦

在你的情况下,罪魁祸首是这条代码:

animation.removedOnCompletion = NO;

通常情况下,人们会误用动画来保留动画完成后的最终值。唯一的问题是通过不从视图中移除动画来实现这一点。核心动画中的动画不会改变它们正在动画的底层属性,它们只是在屏幕上对其进行动画处理。如果您在动画过程中查看实际值,则永远不会看到它发生改变。相反,动画处理的是所谓的呈现层。
通常情况下,当动画完成时,它会从图层中移除,并且呈现层消失,模型层再次出现在屏幕上。但是,当您将动画附加到图层时,屏幕上的所有内容看起来都应该如此,但是您引入了一个差异,即属性所表示的转换方式与图层在屏幕上旋转的方式之间存在差异。
当您将动画配置为可添加时,这意味着要向现有值中“添加”from和to值,就像您所说的那样。问题在于该属性的值为0。您永远不会改变它,只是对其进行动画处理。下次尝试将该动画添加到同一图层时,该值仍然不会更改,但是动画正在按照其配置执行:“从模型的当前值开始添加动画”。
解决方案是跳过那行代码。结果是旋转不会保留。使其保留的更好方法是更改模型。在对旋转进行动画处理之前设置旋转的新结束值,以便在删除动画时模型看起来与应该看起来的一样。
有一个非常方便的CABasicAnimation属性(我将使用它)称为byValue,可用于执行相对动画。可以将其与toValuefromValue组合使用以执行许多不同类型的动画。这些不同的组合都在其文档中指定(在该部分下)。我要使用的组合是:

byValuetoValue 都是非空的。在(toValue - byValue)toValue之间插入。

通过显式设置toValue为0,动画将从“currentValue-byValue”到“current 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"];

免责声明:

我没有运行过这段代码,但相当确定它应该可以正常运行,并且我没有打错任何字。如果有错误,请纠正我。整个讨论仍然有效。


小修正:CGFloat currentAngle = [[self.imgView.layer valueForKeyPath:zRotationKeyPath] floatValue] - Iducool
我从来不知道removeOnCompletion会有这些问题。谢谢您清晰的见解!真是帮了大忙! - daveMac
2
这将导致动画从0.0开始旋转至90度或-90度,然后再旋转回0.0。当动画完成时,它会跳转到模型值。您实际上希望to的值与模型相同。即animation.toValue = @(currentAngle + angleToAdd)。 - sc0rp10n
@DavidRönnqvist 但是我对使用填充模式的理解是必须将removedOnCompletion设置为no。这是正确的吗,还是只适用于向前填充模式? - daveMac
这段代码是被持续调用还是只被调用一次? - apinho
显示剩余4条评论

6

传递角度的增量值,请查看我的代码

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;
  }
}

以上代码仅供参考,尚未经过测试。

5
如果你多次运行代码,animation.removedOnCompletion = NO;将会导致图层中存在许多动画实例,这是一个不好的主意。请查看@DavidRönnqvist提供的最受欢迎的答案,以获得更好的解决方案。请注意不要改变原来的意思。 - Ricardo Sanchez-Saez

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接