CAAnimationは、標準の「animationDidStart:」/「animationDidStop:」メソッド以外のコールバック関数を割り当てるためのメカニズムを提供していません。
オーバーラップする2つのCALayerを利用するカスタムUIControlがあります。このコントロールの目的は、昔ながらのソナーに似ています。最上層のコンテンツには、絶えず回転する画像が含まれています(この層を「ワンド」と呼びます)。そのレイヤーの下には、ワンドがブリップを通過するときにブリップをレンダリングする「spriteControl」レイヤーがあります。
ブリップが表すオブジェクトは、spriteControlによってプリフェッチされ、非表示のCAShapeLayersに編成されます。CABasicAnimationを使用してワンドを一度に10度回転させ、次に「animationDidStop:」メソッドを使用して、ワンドレイヤーの現在の回転値(別名見出し)を取得し、ブリップインおよびフェードアウト効果をシミュレートするための1.0〜0.0。最後に、プロセスは無期限に最初からやり直されます。
CAAnimationコールバックを使用するこのアプローチでは、ワンドが「ping」位置(つまり、10度、20度、270度など)に到達するタイミングが常に他のレイヤーのブリップの照明と一致することが保証されますが、停止の問題があります。 、再計算し、10度ごとにアニメーションを開始します。
NSTimerを生成して、ワンドのプレゼンテーション層の角度を照会して見出し値を取得するメソッドを起動できます。ただし、これにより、ワンドとブリップのハイライトの同期を維持することがより困難になり、および/または一部が完全にスキップされる原因になります。このアプローチについては、ここで少し説明します。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];
}