替代使用 CABasicAnimation 回调?
Posted
技术标签:
【中文标题】替代使用 CABasicAnimation 回调?【英文标题】:Alternative to using CABasicAnimation callbacks? 【发布时间】:2011-03-07 01:33:53 【问题描述】:除了标准的“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 突出显示同步变得更加困难,和/或导致一些被完全跳过。这种方法在这里讨论了一下:How can I callback as a CABasicAnimation is animating?
所以我的问题是,在不使用 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];
【问题讨论】:
更新:我已经通过创建预先计算值的静态数组消除了所有不必要的计算。我现在向自己保证,我的性能瓶颈不是由昂贵的数学运算引起的。然后我在魔杖层上实现了关键帧动画,但仍然得到了一个不太平滑的旋转。我实际上发现在这种情况下切换到 UIView 动画而不是 CAAnimation 可以提供更流畅的动画。到目前为止,似乎从 CAAnimation 获得了任何真正的性能优势,您希望完全避免使用回调,而让 GPU 完成它的工作。 【参考方案1】:假设雷达旋转一圈需要 10 秒。
要让魔杖无限旋转,请将CABasicAnimation
附加到它,并将其Duration
属性设置为10,并将其RepeatCount
属性设置为1e100f。
每个 blip 都可以使用它们自己的实例 CAKeyframeAnimation
进行动画处理。我不会写详细信息,但是对于每个 blip,您指定一组不透明度值(我假设不透明度是您淡出 blip 的方式)和一组时间百分比(请参阅 Apple 的文档)。
【讨论】:
很有趣,感谢您的提示!目前,淡入淡出效果是应用于每个独立运行的 CALayer blip 的动画。我的问题与魔杖更相关,因为我希望它通过的时间来触发各个 blip 图层上的动画。我考虑过设置一个包含 36 个动画的 CAAnimationGroup,每个动画都设置了一个延迟值,以便它们连续运行。我希望这可能会给我每个动画的 animationDidStop 回调。但是 Apple 的文档似乎表明您只能为整个组获得其中一个,而不是其中的每个动画。以上是关于替代使用 CABasicAnimation 回调?的主要内容,如果未能解决你的问题,请参考以下文章
CAAnimation 在发布的 CALayer 上运行回调——Mac OS X
使用 UINavigationController 滑动边缘时 CABasicAnimation 重置