使用CABasicAnimation不止一次旋转UIImageView

Byt*_*Guy 13 core-animation ios

我正在使用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"];
Run Code Online (Sandbox Code Playgroud)

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

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

有任何想法吗?

Dav*_*ist 64

tl;博士:很容易被滥用removeOnCompletion = NO,大多数人都没有意识到这样做的后果.正确的解决方案是更改模型值"for real".


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

什么地方出了错

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

这是非常正确的,它正是发生的事情.从这个意义上说,电脑很有趣.他们总是正是你告诉他们的,而不是你想让他们做什么.

removeOnCompletion = NO 可能是个婊子

在你的情况下,反派是这行代码:

animation.removedOnCompletion = NO;
Run Code Online (Sandbox Code Playgroud)

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

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

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

解决方案

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

byValue 就像魔术一样

有一个非常方便的属性(我将要使用)CABasicAnimation被调用byValue,可用于制作相对动画.它可与组合toValue,并fromValue做许多不同种类的动画.不同的组合都在其文档中指定(在该部分下).我将要使用的组合是:

byValue并且toValue是非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"];
Run Code Online (Sandbox Code Playgroud)

小免责声明:

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

  • 这实际上会导致动画从 0.0 - 90 或 -90 开始,然后旋转到 0.0。当动画结束时,它将跳转到模型值。您实际上希望 to 值与模型相同。即animation.toValue = @(currentAngle + angleToAdd) (2认同)

Idu*_*ool 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;
  }
}
Run Code Online (Sandbox Code Playgroud)

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

  • `animation.removedOnCompletion = NO;`是一个坏主意,因为如果多次运行代码,你将污染具有许多动画实例的图层.请查看@DavidRönnqvist提供的最佳投票答案,以获得更好的解决方案. (5认同)