13

我正在使用 CABasicAnimationUIImageView顺时针旋转 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;会导致进一步的动画使用当前状态作为起点。

有任何想法吗?

4

2 回答 2

67

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


首先:我不是要评判你或对你刻薄。我一遍又一遍地看到同样的误解,我明白它为什么会发生。通过解释事情发生的原因,我希望每个遇到相同问题并看到此答案的人都能更多地了解他们的代码在做什么。

什么地方出了错

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

这是非常正确的,这正是发生的事情。从这个意义上说,计算机很有趣。他们总是按照你告诉他们的,而不是你想让他们做什么。

removeOnCompletion = NO可能是个婊子

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

animation.removedOnCompletion = NO;

在动画完成后保留动画的最终值经常被误用。唯一的问题是它不会从视图中删除动画。Core Animation 中的动画不会改变它们动画的底层属性,它们只是在屏幕上对其进行动画处理。如果您在动画期间查看实际值,您将永远不会看到它发生变化。相反,动画在所谓的表示层上工作。

通常,当动画完成时,它会从图层中移除,表示图层消失,模型图层再次出现在屏幕上。但是,当您将动画附加到图层时,所有内容在屏幕上看起来都应该如此,但是您已经引入了属性所说的变换与图层在屏幕上看起来如何旋转之间的区别。

当您将动画配置为添加时,这意味着 from 和 to 值将添加到现有值中,正如您所说。问题是该属性的值是 0。你永远不会改变它,你只是动画它。下次您尝试将该动画添加到同一图层时,该值仍然不会更改,但动画正在执行其配置的操作:“从模型的当前值添加动画”。

解决方案

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

byValue就像魔术一样

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

byValue并且toValue是非nil. (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"];

小免责声明

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

于 2013-07-02T20:17:13.207 回答
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;
  }
}

以上代码仅供参考。它未经测试

于 2013-07-02T13:47:34.067 回答