使用CABasicAnimation不止一次旋转UIImageView

时间:2013-07-02 13:23:50

标签: ios core-animation

我正在使用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"];

使用上面的代码最初工作,图像保持90度角。如果我再次调用它使其再旋转90度,则动画开始时跳回0并旋转90度,而不是90度到180度。

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

有什么想法吗?

2 个答案:

答案 0 :(得分:64)

tl; dr:很容易误用removeOnCompletion = NO,大多数人都没有意识到这样做的后果。正确的解决方案是将模型值“改为实际”。


首先:我不是在试图判断或吝啬你。我一遍又一遍地看到同样的误解,我可以看出它为什么会发生。通过解释为什么事情发生,我希望每个遇到相同问题并看到这个答案的人都能更多地了解他们的代码在做什么。

出了什么问题

  

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

这是非常正确的,它正是发生的事情。从这个意义上说,电脑很有趣。它们总是与您告诉他们完全相同,而不是您想要他们要做的事情。

removeOnCompletion = NO可能是一个婊子

在你的情况下,恶棍就是这行代码:

animation.removedOnCompletion = NO;

在动画完成后,通常会误用动画的最终值。唯一的问题是它不会从视图中删除动画。 Core Animation中的动画不会改变它们动画的基础属性,它们只是在屏幕上制作动画。如果你在动画期间查看实际值,你将永远不会看到它的变化。相反,动画适用于所谓的表示层。

通常,当动画完成时,它将从图层中移除,并且表示层消失,模型图层再次出现在屏幕上。但是,当您将动画附加到图层时,所有内容都应该在屏幕上显示,但您已经介绍了属性所说的变换与图层在屏幕上的旋转方式之间的区别。

当您将动画配置为添加时,意味着将from和to值添加到现有值,就像您所说的那样。问题是该属性的值为0.您永远不会更改它,只需为其设置动画即可。下次尝试将该动画添加到同一图层时,该值仍然不会被更改,但动画正在完全按照配置的方式执行:“从模型的当前值中添加动画

解决方案

跳过那行代码。结果是旋转不会粘住。让它坚持下去的更好方法是改变模型。在设置旋转动画之前设置旋转的新结束值,以便在移除动画时模型看起来应该如此。

byValue就像魔法

CABasicAnimation上有一个非常方便的属性(我将使用),称为byValue,可用于制作相对动画。它可以与toValuefromValue结合使用,以执行许多不同类型的动画。不同的组合都在其文档中指定(在该部分下)。我将要使用的组合是:

  

byValuetoValue不是 - nil。在(toValue - byValue)toValue之间插值。

一些实际代码

如果显式toValue为0,则动画从“currentValue-byValue”变为“当前值”。通过更改模型,第一个当前值是结束值。

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"];

小免责声明

我没有运行此代码,但相当确定它运行应该并且我没有做任何错别字。如果我这样做,请纠正我。整个讨论仍然有效。

答案 1 :(得分: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;
  }
}

以上代码仅供参考。它没有经过测试