タイマーの使用を提案してくれたk06aに再度感謝します。私は NSTimer の操作についていくつかの研究を行いましたが、他の人にも役立つと思うので、実装を示したいと思います。
したがって、私の場合、Float32 angle;
矢印全体の描画が開始されるメインプロパティであるある角度から始まる曲線矢印を描画する UIButton サブクラスがありました。つまり、角度の値を変更するだけで、矢印全体が回転します。この回転のアニメーションを作成するには、クラスのヘッダー ファイルに次の行を追加します。
NSTimer* timer;
Float32 animationDuration; // Duration of one animation loop
Float32 animationFrameRate; // Frames per second
Float32 initAngle; // Represents the initial angle
Float32 angle; // Angle used for drawing
UInt8 nFrames; // Number of played frames
-(void)startAnimation; // Method to start the animation
-(void)animate:(NSTimer*) timer; // Method for drawing one animation step and stopping the animation
実装ファイルで、アニメーションの継続時間とフレーム レートの値、および描画の初期角度を設定します。
initAngle=0.75*M_PI;
angle=initAngle;
animationDuration=1.5f; // Arrow makes full 360° in 1.5 seconds
animationFrameRate=15.f; // Frame rate will be 15 frames per second
nFrames=0; // The animation hasn't been played yet
アニメーションを開始するには、 1/ animate:(NSTimer*) timer
15 秒ごとにメソッドを呼び出す NSTimer インスタンスを作成する必要があります。
-(void)startAnimation
{
timer = [NSTimer scheduledTimerWithTimeInterval:1.f/animationFrameRate target:self selector:@selector(animate:) userInfo:nil repeats:YES];
}
このタイマーはanimate:
すぐにメソッドを呼び出し、手動で停止されるまで 1/15 秒ごとに繰り返します。
それでは、1 つのステップをアニメーション化するためのメソッドの実装について説明します。
-(void)animate:(NSTimer *)timer
{
nFrames++; // Incrementing number of played frames
Float32 animProgress = nFrames/(animationDuration*animationFrameRate); // The current progress of animation
angle=initAngle+animProgress*2.f*M_PI; // Setting angle to new value with added rotation corresponding to current animation progress
if (animProgress>=1.f)
{ // Stopping animation when progress is >= 1
angle=initAngle; // Setting angle to initial value to exclude rounding effects
[timer invalidate]; // Stopping the timer
nFrames=0; // Resetting counter of played frames for being able to play animation again
}
[self setNeedsDisplay]; // Redrawing with updated angle value
}
最初に言及したいのは、angle==initAngle
丸め効果のために比較線だけが機能しなかったということです。完全に回転した後、それらはまったく同じではありません。そのため、それらが十分に近いかどうかを確認し、角度値を初期値に設定して、アニメーション ループを何度も繰り返した後に角度値の小さなドリフトをブロックします。そして、完全に正しくするために、このコードは角度の変換を常に 0 から 2*M_PI の間で次のように管理する必要があります。
angle=normalizedAngle(initAngle+animProgress*2.f*M_PI);
どこ
Float32 normalizedAngle(Float32 angle)
{
while(angle>2.f*M_PI) angle-=2.f*M_PI;
while(angle<0.f) angle+=2.f*M_PI;
return angle
}
もう 1 つの重要な点は、残念ながら、この種の手動アニメーションに easeIn、easeOut、またはその他のデフォルトの animationCurves を簡単に適用する方法がわからないことです。存在しないと思います。しかし、もちろん、手でそれを行うことは可能です。そのタイミング関数を表す行は
Float32 animProgress = nFrames/(animationDuration*animationFrameRate);
として扱うことができますFloat32 y = x;
。これは、時間の速度と同じである線形動作、一定速度を意味します。y = cos(x)
しかし、それをor y = sqrt(x)
orのように変更してy = pow(x,3.f)
、非線形の動作を与えることができます。x が 0 (アニメーションの開始) から 1 (アニメーションの終了) になることを考慮して、自分で考えることができます。
コードの見栄えを良くするには、独立したタイミング関数を作成することをお勧めします。
Float32 animationCurve(Float32 x)
{
return sin(x*0.5*M_PI);
}
しかし今では、アニメーションの進行状況と時間の間の依存関係は直線的ではないため、アニメーションを停止する指標として時間を使用する方が安全です。(たとえば、矢印を 1.5 回転させて最初の角度に戻すようにしたい場合があります。つまり、animProgress は 0 から 1.5 になり、timeProgress は 0 から 1 になります。)安全のために、時間の進行とアニメーションの進行を分けています。
Float32 timeProgress = nFrames/(animationDuration*animationFrameRate);
Float32 animProgress = animationCurve(timeProgress);
次に、時間の進行状況を確認して、アニメーションを停止するかどうかを決定します。
if(timeProgress>=1.f)
{
// Stop the animation
}
ところで、誰かがアニメーションの便利なタイミング関数のリストを含むソースを知っている場合は、それらを共有していただければ幸いです.
ビルトインの MacOS X ユーティリティ Grapher は関数を視覚化するのに大いに役立ちます。これにより、アニメーションの進行が時間の進行にどのように依存するかを確認できます。
それが誰かを助けることを願っています...