AppleのCore Animationプログラミングガイドを読んだメモ

  • 245
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

Appleが提供しているプログラミングガイドは全部読んだほうがいいなーと思っています。
目下、興味のあるところから順次読み込み中。

今回は[PDF] Core Animationプログラミングガイドを読んだので、覚えておいたほうがいいところとかをピックアップし、自分なりの考察なんかを加えて書いています。


UIView/CALayerの基本構造

画面になにかを表示しようとした時、使うのはUIViewかそのサブクラスを使うのが通常です。
addSubview:メソッドなど使って階層構造を成して構築していきますね。

普通はUIViewを使えば問題ありませんが、描画のパフォーマンスやちょっとした細かい演出などをしたい場合はCALayerを利用します。
やはりどういう構造になって画面に表示されているか、を知るのは大事なことですね。

UIViewは通常、レンダリング周りを担当するCALayerクラスをlayerプロパティに持っています。
layerはいわゆるモデルオブジェクトだと、ガイドラインには書かれています。
レンダリング自体を担当するのではなく、あくまでその情報を保持するモデルとして振る舞う、ということのようです。

3種類のレイヤツリー

レイヤには、以下の3種類のツリー構造があります。

  • モデルレイヤツリー
    いわゆるlayerプロパティとそのサブレイヤの階層構造。アニメーションする際は目標値(つまりアニメーションのゴール)の値を保持します。(まさに「モデル」データ)
  • プレゼンテーションツリー
    アニメーション実行中の、「現在の値」を保持する。アニメーション中の値を参照したい場合はpresentationLayerプロパティで参照することができます。(逆に、これにアクセスできるのはアニメーション実行中のみ)
  • レンダーツリー
    アニメーション処理のためにCore Animationが内部的に使います。

UIViewに関連付けるレイヤーオブジェクトを変更する

通常はCALayerクラスが紐付けられていますが、ビューのlayerClassクラスメソッドをオーバーライドし、変更したいCALayerのサブクラスを返すようにすることで変更することができます。

// `CAEAGLLayer`を返す例
+ (Class) layerClass
{
    return [CAEAGLLayer class];
}

レイヤにコンテンツを提供する

レイヤはビットマップ画像を管理するモデルクラスです。
表示すべき内容はcontentsプロパティが保持しています。

提供する方法

  • contentsプロパティに直接画像オブジェクトを渡す方法
  • デリゲートオブジェクトをレイヤに割り当て、デリゲートオブジェクトで描画する方法
  • レイヤのサブクラスを作り、描画用メソッドをオーバーライドする方法

ビットマップ画像を提供する

通常の画像などを提供する場合は、上記の通りcontentsプロパティにCGImageRef型のデータを渡すことで提供することが可能です。
画像を直接contentsプロパティに渡す場合、画像側のcontentsScaleプロパティに適切な値を設定する必要があります。
(いわゆるRetina対応。Retinaデバイスの場合は2.0を指定する)

デリゲートを使う

デリゲートを使う場合は、displayLayer:メソッドか、drawLayer:inContext:メソッドを実装して描画コンテンツを返すことで対応します。
デリゲートで対応する場合は、少なくともどちらか一方をオーバーライドする必要があります。

drawLayer:inContext:メソッドはその名の通り、現在のコンテキストに描画します。
Core Animation側で適切にコンテキストを生成した後にこのメソッドを呼び出し、メソッドは渡されたコンテキストに描画処理を行うことで対応します。

※両方のメソッドをオーバーライドした場合は、displayLayer:メソッドが使われます。

Appleプログラミングガイドのサンプル
displayLayer:メソッド

displayLayer:メソッドは、アプリケーション側でビットマップ画像のロードや事前準備しておく場合などに有用です。

- (void)displayLayer:(CALayer *)theLayer
{
    // 状態プロパティの値を検査
    if (self.displayYesImage) {
        // 「Yes」画像を表示
        theLayer.contents = [someHelperObject loadStateYesImage];
    }
    else {
        // 「No」画像を表示
        theLayer.contents = [someHelperObject loadStateNoImage];
    }
}
drawLayer:inContext:メソッド

一方、drawLayer:inContext:メソッドは実行時に動的に描画を行うことが可能です。

- (void)drawLayer:(CALayer *)theLayer inContext:(CGContextRef)theContext
{
    CGMutablePathRef thePath = CGPathCreateMutable();

    CGPathMoveToPoint(thePath, NULL, 15.0f, 15.0f);
    CGPathAddCurveToPoint(thePath, NULL,
                             15.0f, 250.0f,
                            295.0f, 250.0f,
                            295.0f,  15.0f);

    CGContextBeginPath(theContext);
    CGContextAddPath(theContext, thePath);
    CGContextSetLineWidth(theContext, 5);
    CGContextStrokePath(theContext);

    // パスを解放
    CFRelease(thePath);
}

プログラミングガイドからの引用

自動レイヤつきビューに独自のコンテンツを表示したい場合は、当該ビューの描画メソッドをオーバーライドしてください。すると、ビューは自動的に、対応するレイヤのデリゲートを生成し、必要なメソッドを実装するようになっています。この仕組みは変更できません。したがって、コンテンツを描画するためには、ビューのdrawRect:メソッドを実装することになります。

※自動レイヤ付きビューは、iOSの場合はすべてのViewです。

サブクラスで対応する

サブクラスでは、以下の方法でコンテンツを提供することができます。

  • displayメソッドをオーバーライドし、直接contentsプロパティにコンテンツを格納する
  • drawInContext:メソッドをオーバーライドし、引数のコンテキストに直接描画を実行する

レイヤの視覚スタイルや外観を調整する

レイヤの視覚スタイルや外観については、以前[iOS] CALayerが便利そうという記事にちょっとだけ書いたのでこちらを見てください。


独自のプロパティをレイヤに追加する

キー値コーディングの仕組みを利用して、独自のプロパティが追加できるようになっています。
また、これで設定したプロパティにアクションを関連付けて、任意のプロパティが変化した際に対応するアニメーションが動き出すようにすることも可能です。


レイヤのプロパティ変化をアニメーション表示する

レイヤのプロパティへの変更は、即時に変化しますが外観は変化しません。(即時に変化するのは保持する値です)
変化はアニメーションによって実行され、徐々に変化していきます。
これは暗黙のアニメーションです。
従って、ごく簡単な変化やあまりコードを記述したくない場合は暗黙のアニメーションを使うとコードがシンプルになります。

CALayerを新規で作成し、既存のUIViewのレイヤにaddSublayer:で追加したレイヤの場合にアニメーションします。おそらくUIViewクラスがアニメーション機能をオフにするためでしょう。

一方、明示的に簡単なアニメーションをさせたい場合はCABasicAninmationを利用します。

Appleのプログラミングガイドのサンプル

CABasicAnimation *fadeAnim = [CABasicAnimation animationWithKeyPath:@"opacity"];
fadeAnim.fromValue = [NSNumber numberWithFloat:1.0];
fadeAnim.toValue   = [NSNumber numberWithFloat:0.0];
fadeAnim.duration = 1.0;
[theLayer addAnimation:fadeAnim forKey:@"opacity"];

// レイヤの実プロパティを最終値に変更
theLayer.opacity = 0.0;

明示的なアニメーションの場合、実際の値は変更されません。あくまでアニメーション用の値として使われるだけです。
そのため、アニメーション後に変更を固定したい場合はサンプルの最終行のように変化後の値を設定しておく必要があります。

addAnimation:forKey:forKeyは、あとでアニメーションを途中で削除する場合などにアニメーションを指定する際に利用します。
(つまり任意の文字列・アニメーション名)


キーフレームアニメーションを使ってレイヤのプロパティを変更する

CAKeyframeAnimationオブジェクトを使うことで、上記のシンプルな値の変化をさせるアニメーションだけではなく、非線形に変化するアニメーションも実現することができます。
また、レイヤの位置については指定したパスに沿って移動させることもできます。

Appleのプログラミングガイドのサンプル

// 2つの弧(跳ね返りの軌跡)を表すパス(CGPath)を生成
CGMutablePathRef thePath = CGPathCreateMutable();
CGPathMoveToPoint(thePath, NULL, 74.0, 74.0);
CGPathAddCurveToPoint(thePath, NULL,
                         74.0, 500.0,
                        320.0, 500.0,
                        320.0,  74.0);
CGPathAddCurveToPoint(thePath, NULL,
                        320.0, 500.0,
                        566.0, 500.0,
                        566.0,  74.0);

CAKeyframeAnimation *theAnimation;

// positionプロパティをキーパスとして指定し、アニメーションオブジェクトを生成
theAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
theAnimation.path     = thePath;
theAnimation.duration = 5.0;

// アニメーションをレイヤに追加
[theLayer addAnimation:theAnimation forKey:@"position"];

キーフレーム値を指定する

キーフレーム値はキーフレームアニメーションの要です。
キーフレーム値はオブジェクトの配列として指定するのが普通ですが、CGPoint型のデータを含む値(例えばanchorPointpositionプロパティなど)の場合はCGPathRef型のデータも指定可能です。

配列を準備する場合、いくつかの注意点があります。
プログラミングガイドから引用します。

  • CGRect型のプロパティ(bounds、frameなど)はNSValueオブジェクトにラップします。
  • transformプロパティの場合は、CATransform3D型の行列をNSValueオブジェクトにラップします。この場合、レイヤに各変換行列を適用した結果がキーフレームになります。* borderColorプロパティの場合は、CGColorRef型の各データをid型にキャストして配列に追加します。* CGFloat型の値を取るプロパティについては、値をNSNumberオブジェクトにラップして配列に追加します。* レイヤのcontentsプロパティをアニメーション化する場合は、CGImageRef型の配列を指定します。

CGPoint型の値を取るプロパティについては、(NSValueオブジェクトにラップした)点構造体の配列を生成するか、またはCGPathRefオブジェクトでパスを指定してください。点の配列で指定すると、各点を直線で結んだ折れ線のパスに沿って動くアニメーションになります。CGPathRefオブジェクトであれば、その起点からパスに沿って動くようになり、曲面に沿った動きにすることも可能です。また、開いたパスでも閉じたパスでも構いません。

calculationModeプロパティ

calculationModeプロパティは、各キーフレーム間のタイミングの計算に用いるアルゴリズムを表します。

mode 意味
kCAAnimationLinear 指定されたtimingFunctionに応じて線形に補完する
kCAAnimationCubic 指定されたtimingFunctionに応じて補完する
kCAAnimationPaced 等速で補完する。指定されたkeyTimestimingFunctionsは無視される
kCAAnimationCubicPaced (調査中)
kCAAnimationDiscrete 離散(discrete)アニメーション。キーフレーム間は補完されずに一気に変化する

keyTimesプロパティ

各キーフレーム値を適用する時刻マーカを指定します。
calculationModekCAAnimationLinearkCAAnimationDiscretekCAAnimationCubicのときのみ有効)

timingFunctionsプロパティ

隣接するキーフレーム間に適用されるタイミング曲線を指定します。
(ease-inやease-outのような)


明示的アニメーションを実行中に停止する

アニメーションを停止する方法がいくつかあります。

  • removeAnimationForKey:メソッドで、指定したキーのアニメーションを削除する
  • removeAllAnimationsメソッドですべてのアニメーションを削除する

※削除されたアニメーションはレイヤが保持している現在値を元に再描画します。


CAAnimationGroupオブジェクトを使って複数のアニメーションを実行する

上記のアニメーションは単一のキーに対するアニメーションでした。
これを複数(例えば画面外に移動しながらフェードアウト、のような)のアニメーションを実装したい場合に使います。

CAAnimationGroupオブジェクトはグループ化に特化していて、仮に個別のCAKeyframeAnimationオブジェクトにタイミングなどを指定していても、グループ側の設定が優先されます。

Appleプログラミングガイドのサンプル

// アニメーション1
CAKeyframeAnimation *widthAnim = [CAKeyframeAnimation animationWithKeyPath:@"borderWidth"];

NSArray *widthValues = [NSArray arrayWithObjects:@1.0, @10.0, @5.0, @30.0, @0.5, @15.0, @2.0, @50.0, @0.0, nil];
widthAnim.values = widthValues;
widthAnim.calculationMode = kCAAnimationPaced;

// アニメーション2
CAKeyframeAnimation *colorAnim = [CAKeyframeAnimation animationWithKeyPath:@"borderColor"];

NSArray* colorValues = [NSArray arrayWithObjects:(id)[UIColor greenColor].CGColor, (id)[UIColor redColor].CGColor, (id)[UIColor blueColor].CGColor, nil];
colorAnim.values = colorValues;
colorAnim.calculationMode = kCAAnimationPaced;

// アニメーショングループ
CAAnimationGroup *group = [CAAnimationGroup animation];
group.animations = [NSArray arrayWithObjects:colorAnim, widthAnim, nil];
group.duration   = 5.0;

[myLayer addAnimation:group forKey:@"BorderChanges"];

アニメーションの終了を検出する

アニメーションには、開始/終了を検出する仕組みがあります。
アニメーションの状態を検出する方法は2通りあります。

  • setCompletionBlock:メソッドで、アニメーションが終了した段階で呼ばれるblockを渡す
  • CAAnimationオブジェクトにデリゲートを設定し、animationDidStart:メソッドとanimationDisStop:finished:メソッドを実装する

UIViewをアニメーションさせる

UIViewは通常、プロパティを変更しても自動的にアニメーションされません。
UIViewをアニメーションさせる場合は、UIView#animateWithDuration:animations:クラスメソッドを利用します。

animations引数はblockを渡し、その中でアニメーションさせたいプロパティの値を変化させます。
これは、このblock内に限りアニメーションを有効にすることになるためです。

Appleプログラミングガイドのサンプル

[UIView animateWithDuration:1.0 animations:^{
    // 不透明度を暗黙に変更
    myView.layer.opacity = 0.0;

    // 位置を明示的に変更
    CABasicAnimation *theAnim = [CABasicAnimation animationWithKeyPath:@"position"];
    theAnim.fromValue = [NSValue valueWithCGPoint:myView.layer.position];
    theAnim.toValue   = [NSValue valueWithCGPoint:myNewPosition];
    theAnim.duration  = 3.0;
    [myView.layer addAnimation:theAnim forKey:@"AnimateFrame"];
}];

レイヤ階層にレイヤを配置する

レイヤは、UIViewと似たような階層構造を形成します。
子のレイヤを「サブレイヤ」、親のレイヤを「スーパーレイヤ」と呼びます。UIViewの関係と同じです。
表示順もUIViewと同様に、あとから追加したものが上に来ますが、zPositionプロパティが設定されている場合はそれを優先します。


レイヤ階層がアニメーションに与える影響

speedプロパティはアニメーション速度を指定するプロパティです。
このプロパティを(例えば)2.0にした場合は速度が2倍のアニメーションになります。
そしてこのプロパティは子レイヤにも設定されている場合は累積となります。

つまり、スーパーレイヤで2.0を指定し、さらに子レイヤにも2.0を指定すると、子レイヤは累積4.0倍のスピードで動くことになります。
プロパティの中にはこうした子レイヤに影響を与えるものがあります。


レイヤ間で座標値を変換する

各レイヤはいわゆる座標空間を持ち、子レイヤに影響を与えます。
(例えば、親レイヤが移動すれば自動的に子レイヤも移動する)
つまり子レイヤは「相対位置」で表されます。

しかし、例えばタッチ位置などは画面全体の中での座標を示します。
この位置に調べたい子レイヤがあるか、といったチェックをする際に座標値を変換する必要が出てきます。

座標変換については以前に記事を書いているのでそちらをご覧ください。

変換のためのメソッドだけ列挙しておきます。

  • convertPoint:fromLayer:
  • convertPoint:toLayer;
  • convertRect:fromLayer;
  • convertRect:toLayer;

レイヤの持つ時間空間

レイヤにはそれぞれ独自に時間空間を管理しています。
時間はアニメーションの開始や終了時刻を、システムの他の部分と同期するようになっています。
この独自管理の時間空間も、座標変換と同様に変換用のメソッドが用意されています。

  • convertTime:fromLayer;
  • convertTime:toLayer;

通常はどの時間も同期していますが、あるレイヤでアニメーション速度を変更するなどすると時間にずれが生じます。
アニメーションの終わりを合わせたい場合などは、時間空間を変換することで時間をあわせることができるようになります。


高度なアニメーション

遷移アニメーション

遷移アニメーションオブジェクト(CATransition)を利用すると、いわゆる遷移アニメーションを実装することが出来ます。
また遷移アニメーションは、開始/終了時点の値(startProgressendProgress)を設定することで、アニメーションを途中で終わらせることができます。

Appleプログラミングガイドのサンプル

myView1のみが可視状態となっている前提です。

CATransition *transition = [CATransition animation];
transition.startProgress = 0;
transition.endProgress   = 1.0;
transition.type          = kCATransitionPush;
transition.subtype       = kCATransitionFromRight;
transition.duration      = 1.0;

// 2つのレイヤに遷移アニメーションを追加
[myView1.layer addAnimation:transition forKey:@"transition"];
[myView2.layer addAnimation:transition forKey:@"transition"];

// 最後に各レイヤの表示/非表示を更新
myView1.hidden = YES;
myView2.hidden = NO;

typesubtypeに指定できる定数は以下の通り。名前から推測できますね。

定数一覧

type
  • kCATransitionFade
  • kCATransitionMoveIn
  • kCATransitionPush
  • kCATransitionReveal
subtype
  • kCATransitionFromRight
  • kCATransitionFromLeft
  • kCATransitionFromTop
  • kCATransitionFromBottom

アニメーションのタイミングを調整する

CFTimeInterval localLayerTime = [myLayer convertTime:CACurrentMediaTime() fromLayer:nil];

fillModekCAFillModeBackwards

beginTimeプロパティ
autoreversesプロパティ
repeatCountプロパティ … 1.5で終了値で終わる
timeOffsetプロパティ


アニメーションを一時停止/再開する

アニメーションを一時停止したい場合、レイヤがCAMediaTimingプロトコルに準拠していることを利用し、速度を0.0にすることで擬似的に一時停止させることができます。
再開したい場合は速度を0以外にすれば再開することができます。

Appleプログラミングガイドのサンプル

- (void)pauseLayer:(CALayer *)layer
{
    CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
    layer.speed = 0.0;
    layer.timeOffset = pausedTime;
}
- (void)resumeLayer:(CALayer *)layer
{
    CFTimeInterval pausedTime = [layer timeOffset];
    layer.speed      = 1.0;
    layer.timeOffset = 0.0;
    layer.beginTime  = 0.0;
    CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
    layer.beginTime = timeSincePause;
}

サンプルに対してのメモ

停止部分

上が停止のコード、下が再開のコードです。
やっていることは、停止時にまず、現在時刻をtimeOffsetプロパティに設定した上で、speedプロパティを0に設定します。こうすることでアニメーションが停止します。

おそらく、speedを0にしつつtimeOffsetに現在時刻を入れることで当分先になるようオフセットを設定しているのだと思います。

再開部分

続いて再開部分は、まずspeedを1にし、timeOffsetを0にします。
beginTimeに設定しているのは「実際の」時間です。(とある時点からの秒数? つまり、相対時間ではなく、絶対時間的な感覚で時間を指定します。例えば「何時何分から開始」のような)

最初、オフセットのように「5秒後に開始」させようとしてlayer.beginTime = 5;としてもまったく動かず悩んだんですが、上で書いた通り実際の時間を指定するようです。つまり、「現在の時間+5秒」とする必要がある、ということです。

解説

ちなみに下のコードでtimeOffset値を取り出し、とあるレイヤの現在時刻から引いているのは、timeOffsetの設定により 現在時刻が未来になっている ためです。

つまり該当行時点では現在時刻の倍の時間が進んだ状態になっている、ということです。(ローカル時間空間的に)
そこで、その時間から現在時刻を引くことで、未来から現在に戻している、という処理になります。


明示的なトランザクションでアニメーションパラメータを変更できる

複雑なアニメーションを管理するために、CATransactionクラスが用意されています。
(例えばグループ化や開始時間の管理など)
基本的に、レイヤに追加されたアニメーションは暗黙のトランザクションが生成され管理されます。

明示的にトランザクションを生成するコード例(Appleプログラミングガイドから引用)

[CATransaction begin];
theLayer.zPosition = 200.0;
theLayer.opacity = 0.0;
[CATransaction commit];

トランザクションに対するパラメータ(durationなど)を変更する場合はsetValue:forKey:メソッドを使って変更する必要があります。

[CATransaction begin];
[CATransaction setValue:[NSNumber numberWithFloat:10.0f]
                 forKey:kCATransactionAnimationDuration];

トランザクションの入れ子

トランザクションは入れ子にすることができます。(beginメソッドとcommitメソッドを対にして入れ子にする)
入れ子にすることで、トランザクションごとに設定を施すことができ、個別のアニメーションをさせることが可能になります。


アニメーションに遠近感を与える

レイヤは通常、単純化のため平行投影して表示されます。(要は遠近感(パース)がない表示モード)
しかし、遠近感を表現する座標変換行列を用いることで、3D空間の遠近感を演出することが可能です。
この遠近感を出す方法は、スーパーレイヤのsublayerTransformプロパティに適切な変換行列を設定します。
sublayerTransformプロパティは、自身には影響なく、子レイヤに対して変換行列が適用されます)
遠近感を出す設定は以下の通りです。

Appleプログラミングガイドのサンプル

CATransform3D perspective = CATransform3DIdentity;
perspective.m34 = -1.0 / eyePosition;

// 変換を親レイヤに適用
myParentLayer.sublayerTransform = perspective;

m34は行列の3行4列目を表す要素です。
遠近感(パースペクティブ)を出すのはこの値を適切に設定することでいわゆる「パース」がつくようになります。
eyePositionの値を色々変えてみるとパースの付き方が確認できます。(500〜600が人間が見るのと同じくらいのパースだと思います)


レイヤのデフォルトの動作を変更する

Core Animationは「アクションオブジェクト」を使ってレイヤの暗黙のアニメーション動作を実装しています。
アクションオブジェクトはCAActionプロトコルに準拠したオブジェクトで、レイヤ上で実行されるなにかしらの動作(アクション)を定義するオブジェクトです。

アクションオブジェクトはCAActionプロトコルに準拠する

独自のアクションオブジェクトを生成するためには、CAActionプロトコルに準拠し、runActionForKey:object:arguments:メソッドを実装する必要があります。

アクションを起動する方法を決める

アクションオブジェクトを定義するだけでなく、その起動も決める必要があります。
アクションの起動が設定できるのは以下になります。(Appleプログラミングガイドから引用)

  • レイヤのあるプロパティ値が変化したとき。アニメーション化可能なプロパティでなくても構いません(さらに、独自にレイヤに追加したプロパティであっても可)。このアクションを識別するキーは、当該プロパティの名前と同じです。* レイヤが可視になった、あるいはレイヤ階層に追加されたとき。このアクションを識別するキー はkCAOnOrderInです。* レイヤがレイヤ階層から削除されたとき。このアクションを識別するキーはkCAOnOrderOutです。* レイヤが遷移アニメーションに関与しようとしているとき。このアクションを識別するキーは kCATransitionです。

アクションオブジェクトを対象レイヤに組み込む

生成したアクションをレイヤに組み込むと、Core Animationはアクションオブジェクトを以下の順番で検索します。(Appleプログラミングガイドから引用します)

  1. レイヤにデリゲートがあってactionForLayer:forKey:メソッドが実装されていれば、これを呼び出します。デリゲートは次のいずれかを実行しなければなりません。 * 与えられたキーに対応するアクションオブジェクトを返す。 * nilを返す。アクションを処理できない旨を表し、この場合、検索を続行することになります。 * NSNullオブジェクトを返す。この場合、検索を直ちに打ち切ることになります。2. 与えられたキーで、レイヤのactions辞書を検索します。3. style辞書から、与えられたキーを含むactions辞書を検索します。詳しく言うと、style辞書には actionsキーがあり、その値は再び辞書になっています。レイヤはこの辞書から、与えられたキーに対する値を検索するのです。4. クラスメソッドdefaultActionForKey:を呼び出します。5. Core Animationに定義された、暗黙のアクションがあればそれを実行します。

Appleプログラミングガイドのサンプル

- (id<CAAction>)actionForLayer:(CALayer *)theLayer
                        forKey:(NSString *)theKey
{
    CATransition *theAnimation = nil;

    if ([theKey isEqualToString:@"contents"]) {
        theAnimation = [[CATransition alloc] init];
        theAnimation.duration = 1.0;
        theAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
        theAnimation.type = kCATransitionPush;
        theAnimation.subtype = kCATransitionFromRight;
    }

    return theAnimation;
}

このメソッドはレイヤのプロパティが変化した際などに呼び出されます。
上記サンプルでは、contentsプロパティが変化した場合のみ、トランジションオブジェクトを生成してそれを返しています。


CATransactionを使って一時的にアクションを無効にする

以下のようにすることで、暗黙のトランザクションによるアニメーションを無効化できます。

[CATransaction begin];

[CATransaction setValue:(id)kCFBooleanTrue
                 forKey:kCATransactionDisableActions];
[aLayer removeFromSuperlayer];

[CATransaction commit];

描画・アニメーション性能の改善

できるだけ不透明なレイヤを使う

layer.opaqueプロパティにYESを指定する

同じ内容のレイヤにはコンテンツを明示的に設定する

同じ画像を表示するレイヤに関しては、レイヤのcontentsプロパティに画像を直接指定します。
すると、レイヤはバッキングストアとして設定された画像を直接使うため、メモリを節約できます。

レイヤの大きさは常に整数で設定する

必要であれば非同期にレイヤをレンダリングする

デリゲートのdrawLayer:inContext:メソッドやビューのdrawRect:メソッドは通常、メインスレッドで同期的に実行されます。
しかし同期的に処理すると重い場合などはdrawsAsynchronouslyプロパティを有効にして、バックグラウンドスレッドで処理することができます。