CALayerのプロパティをアニメーションをさせるとき、大抵の場合CABasicAnimationを使うと思いますが、CABasicAnimationは増加値が基本的に単調増加です。timingFunctionを設定すると二次ベジェ曲線で増加の振る舞いを変えることができますが、正弦波のような波の関数には出来ません。
正弦波は、たとえば、ホバリングやシェイクアニメーションを実装したい時に使います。
ぷかぷか浮いたり沈んだり、といった感じですね。
そこでCABasicAnimationではなく、CAKeyFrameAnimationを使います。
CAKeyFrameAnimationはその名のとおり、終了までのkeytimeとその時点での値を設定することで、より柔軟なアニメーション表現が可能になります。つまり、アニメーションの値を関数で表現することができるようになります。
ハイ、そこでこんなコードを書きました。
- (void)sinAnimation
{
// アニメーションの開始点
CGPoint start = CGPointMake(self.view.bounds.size.width/2, 100);
// アニメーションの時間
CFTimeInterval duration = 2;
// fps
NSUInteger fps = 24;
// 実際のフレーム数
NSUInteger frameCount = (NSUInteger)duration*fps;
// keytimes
NSMutableArray *keytimes = [NSMutableArray arrayWithCapacity:frameCount];
// values
NSMutableArray *values = [NSMutableArray arrayWithCapacity:frameCount];
// 総フレーム数に対して正規化されたkeytimeの値を割り振る
for (double i = 0, add = (double)1/frameCount; i <= 1.0; i+= add){
[keytimes addObject:@(i)];
}
// 波長
CGFloat amp = 50;
// 加算するラジアン
CGFloat add = M_PI*2/frameCount;
// keytimesに対してサイン関数の値の座標を割り振る
for (NSUInteger i = 0; i < frameCount; i++){
CGFloat x = amp*sin((double)i*add);
CGPoint p = CGPointMake(start.x+x, start.y);
[values addObject:[NSValue valueWithCGPoint:p]];
}
// KeyFrameAnimationを作る
CAKeyframeAnimation *kfa = [CAKeyframeAnimation animationWithKeyPath:@"position"];
kfa.duration = duration;
kfa.keyTimes = keytimes;
kfa.values = values;
// サイクルアニメーションにする
kfa.repeatCount = HUGE_VALF;
// 実行
[self.layer addAnimation:kfa forKey:nil]
}
これでこんな感じにアニメーションします
ファンクショナルなアニメーションをしたいときはCAKeyFrameAnimationを使うといいと思います。