2011年5月27日金曜日

CALayer での画像切り替えアニメ

ちょっとCALayerの設計を舐めていたかもしれない。
自前でOpenGLを使ったアニメエンジンを作って描画をしていたが、
CALayerは描画位置、繰り返し指定、グラフィックの差し替えなども出来るし、
アニメの終了タイミングを取得することもできる。
しかもそれはスレッドを意識しないでも良い形で可能なのだ。
(まぁ実はスレッドを意識しないと納得いかない問題もあるんだけど)
とりあえずコードを載せよう
---xxxview.h---
CALayer *Pic_Layer; // 画像を載せるレイヤー
NSMutableArray *Images; // アニメ画像を保存する配列

--xxxview.m---
どこかの初期化関数で
// イメージ配列を作る
Images = [[NSMutableArray alloc] init];
[Images addObject:(id) [UIImage imageNamed:@"img0.png"].CGImage];
[Images addObject:(id) [UIImage imageNamed:@"img1.png"].CGImage];
[Images addObject:(id) [UIImage imageNamed:@"img2.png"].CGImage];
[Images addObject:(id) [UIImage imageNamed:@"img3.png"].CGImage];

Pic_Layer = [[CALayer alloc] init];
Pic_Layer.frame = CGRectMake(0,0,120,120); // サイズ
[Pic_Layer setPosition:CGPointMake(62,61)]; // 場所
[Pic_Layer setContents:[Images objectAtIndex:0]]; // Imagesの0番目のイメージを初期イメージにする

---touchesBeganとかであたり判定した後にアニメーション開始------
[Pic_Layer setContents:[Images objectAtIndex:[Images count]-1]];

[CATransaction begin];

CAKeyframeAnimation *anim = [CAKeyframeAnimation animation];
anim.keyPath = @"contents";
anim.duration = 1.0f;
anim.values = Images;
anim.calculationMode = kCAAnimationDiscrete;
anim.repeatCount = 1;

[Pic_Layer addAnimation:anim forKey:@"contents"];
[CATransaction commit];

---deallocとかで解放---
[Pic_Layer release];
[Images release];

これやると、配列に指定していた画像の通りにアニメをしてくれる。
ついでに場所を変える事も出来る。(場所、フィルターに関する例はあちこちにある)

自前のアニメエンジンを作らなくても大体の事はプロパティアニメで出来ちゃうんじゃないだろうか

きっと誰でもが気になるのは
ココだろうと思う

[Pic_Layer setContents:[Images objectAtIndex:[Images count]-1]];

anim はスレッドの中で生成されて消えるので、アニメが終わったら contents の変更も無かった事になっちゃうらしい。
だから、Imagesはメンバに持っておいて消えないようにしておいた上で、
アニメの最初の段階で、最後の画像イメージをsetContentsしてしまえば、
アニメが終わったあとには最後のイメージが保持出来る。
やらないと、0、1、2、3までアニメした後に0に戻っちゃうのである。
なんか妙な話なんだけど、
devForum見てても別スレッドでsetContentsしてて全然絵が変わらないよ〜と言ってる人がいたので、
多分そういう事なんだろうと思う。
(冒頭で述べたスレッドを意識しないと納得いかない部分)

別スレッドとはいえ、setContentsしたはずなのに元に戻ってしまうというのは、
ちょっとまったくもって納得いかないので
なにか知ってる人がいたら教えて欲しい。

(追記)
以下2行を追加すると、最後のパターンが残るようになるのが分かった。
anim.removedOnCompletion = NO;
anim.fillMode = kCAFillModeForwards;

0 件のコメント: