除了标准的“animationDidStart:”/“animationDidStop:”方法之外,CAAnimation 不提供分配回调函数的机制。
我有一个自定义 UIControl,它使用了 2 个重叠的 CALayers。这种控制的目的类似于老式的声纳。顶层的内容包含一个不断旋转的图像(将此层称为“魔杖”)。在该层之下是一个“spriteControl”层,当魔杖经过它们时会呈现光点。
blips 表示的对象由 spriteControl 预取并组织成不可见的 CAShapeLayers。我正在使用 CABasicAnimation 一次将魔杖旋转 10 度,然后利用“animationDidStop:”方法调用 spriteControl 上的一个方法,该方法采用魔杖层的当前旋转值(又名标题)并从1.0 到 0.0 用于模拟闪烁和淡出效果。最后,该过程无限期地重新开始。
虽然这种使用 CAAnimation 回调的方法确保了魔杖到达“ping”位置(即 10 度、20 度、270 度等)的时间始终与另一层中光点的照明一致,但存在停止的问题,重新计算,每 10 度启动一次动画。
我可以生成一个 NSTimer 来触发一个方法,该方法查询魔杖表示层的角度以获取航向值。然而,这使得保持魔杖和 blip 突出显示同步变得更加困难,和/或导致一些被完全跳过。这种方法在这里进行了一些讨论:如何在 CABasicAnimation 动画时回调?
所以我的问题是,在不使用 OpenGL ES 重新实现控件的情况下,我是否可以做任何事情来提高魔杖层旋转的性能。(我意识到这在 OpenGL 环境中很容易解决,但是,在这里使用它需要进行大量的重新设计,这根本不值得。)虽然性能问题很小,但我无法摆脱这种感觉我可以做一些简单而明显的事情,这将允许魔杖无限期地动画,而不会暂停在两者之间执行昂贵的旋转计算。
这是一些代码:
- (void)rotateWandByIncrement
{
if (wandShouldStop)
return;
CGFloat newRotationDegree = (wandRotationDegree + WAND_INCREMENT_DEGREES);
if (newRotationDegree >= 360)
newRotationDegree = 0;
CATransform3D rotationTransform = CATransform3DMakeRotation(DEGREES_TO_RADIANS(newRotationDegree), 0, 0, 1);
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform"];
animation.toValue = [NSValue valueWithCATransform3D:rotationTransform];
animation.duration = WAND_INCREMENT_DURATION;
animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = FALSE;
animation.delegate = self;
[wandLayer addAnimation:animation forKey:@"transform"];
}
- (void)animationDidStart:(CAAnimation *)theAnimation
{
if (wandShouldStop)
return;
NSInteger prevWandRotationDegree = wandRotationDegree - WAND_INCREMENT_DEGREES;
if (prevWandRotationDegree < 0)
prevWandRotationDegree += 360;
// Pulse the spriteControl
[[self spriteControl] pulseRayAtHeading:prevWandRotationDegree];
}
- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag
{
// update the rotation var
wandRotationDegree += WAND_INCREMENT_DEGREES;
if (wandRotationDegree >= 360)
wandRotationDegree = 0;
// This applies the rotation value to the model layer so that
// subsequent animations start where the previous one left off
CATransform3D rotationTransform = CATransform3DMakeRotation(DEGREES_TO_RADIANS(wandRotationDegree), 0, 0, 1);
[CATransaction begin];
[CATransaction setDisableActions:TRUE];
[wandLayer setTransform:rotationTransform];
[CATransaction commit];
//[wandLayer removeAnimationForKey:@"transform"];
[self rotateWandByIncrement];
}