Core Animation のはまりどころ
Nov 21, 2010Core Animation でつまずいた点。
その1:暗黙的なトランザクション
Core Animation には暗黙的なトランザクションと呼ばれる特徴があり、特に何も指定しなくてもアニメーションが実行されてとても便利です。
theLayer.opacity = 0; // これだけで勝手にフェードアウトのアニメーションになる
便利は便利なんですが、アニメーションさせたくない場合もあって、そういった時は CATransaction を用いて暗黙的なアニメーションを無効にします。
[CATransaction begin]; [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions]; // このブロックの中でプロパティを書き換えるとアニメーションは起こらない [CATransaction commit];
基本的にはこれでいいのですが、以下のような例ではパフォーマンスに問題があります。
// CALayer継承クラス @interface ParticleLayer : CALayer { ... -(void) move { [CATransaction begin]; [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions]; self.position = CGPointMake(x, y); [CATransaction commit]; }
このクラスは CALayer を継承していて、move メソッドを呼ぶと動く、という作りになっています。そしてこのクラスのインスタンスは、パーティクルとして大量生産されます。
このインスタンスたちをいっぺんに動かすとかなり重いです。
これは、1回の CATransaction ごとに描画プロセスが走るから(たぶん)です。CATransaction を使う場合は、一括で行うべきです。
// パーティクルレイヤーを内包するビュークラス @interface ParticleContainView : UIView { ... -(void) move { [CATransaction begin]; [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions]; for (int i=0; i<[particles count]; i++) { ParticleLayer *layer = (ParticleLayer *)[particles objectAtIndex:i]; [layer move]; } [CATransaction commit]; }
この例では、パーティクルを内包するビュークラスで一括にトランザクションを行っています。
ちなみに Apple のドキュメントではこう説明されています。
レイヤへの変更はすべて、トランザクションの一部として行われます。CATransactionは、複数のレイヤツリーへの変更を描画ツリーへのアトミックな更新として一括処理する役割を果たすCore Animationクラスです。
ぼくの読解力の問題かもしれませんが、一見何のこっちゃわかりません...
が、ようはそういう事なのでしょう。
その2:アニメーションが終了すると元に戻ってしまう
CALayer はプロパティの変更だけでお手軽にアニメーションを実現しますが、複雑なアニメーションには CAAnimation およびその継承クラスを使う事になります。ところが CAAnimation を使って、例えば透明度を0にするアニメーションを設定して実行し、アニメーションが終わると元の透明度に戻ってしまいます。
CABasicAnimation *opacityAnime = [CABasicAnimation animationWithKeyPath:@"opacity"]; opacityAnime.duration = 1; opacityAnime.fromValue = [NSNumber numberWithFloat:1]; opacityAnime.toValue = [NSNumber numberWithFloat:0]; [aLayer addAnimation:opacityAnime forKey:@"animeFadeOut"];
ググったりドキュメントを見ても、なかなかこの現象への言及が見つからなかったのですが、結論としては、removedOnCompletionとfillModeを設定すれば意図した通りに動きます。
CABasicAnimation *opacityAnime = [CABasicAnimation animationWithKeyPath:@"opacity"]; opacityAnime.duration = 1; opacityAnime.fromValue = [NSNumber numberWithFloat:1]; opacityAnime.toValue = [NSNumber numberWithFloat:0]; opacityAnime.removedOnCompletion = NO; opacityAnime.fillMode = kCAFillModeForwards; [aLayer addAnimation:opacityAnime forKey:@"animeFadeOut"];
Comments