プログラミングの話題は「yuyarinのtopcoder記」で!
僕を追いかけたい方は Twitter へ!
2010年07月09日
iOS4 での UIView のアニメーション
最近研究室の色々で地図関係の iPhone アプリを開発しているのだけど,Map のように自分の位置に青い丸いのを表示して,その周囲に波紋のようなものをアニメーションで表示したいと思って試行錯誤してみた.
UIView のアニメーションについては,iOS4 以降では animateWithDuration:delay:options:animations:completion などの block-based animation methods が推奨されている.従来のbeginAnimations:context: や setAnimationDuration: や setAnimationTransition:forView:cache: は推奨されていない(discouraged).
// block-based animation methods for iOS4 + animateWithDuration:animations: + animateWithDuration:animations:completion: + animateWithDuration:delay:options:animations:completion: + transitionFromView:toView:duration:options:completion: + transitionWithView:duration:options:animations:completion:
この block-based animation methods では
[UIView animateWithDuration:1.5f // 1.5秒おきに
delay:0.0f // 0.0秒後から
options:UIViewAnimationOptionRepeat // 永遠に繰り返す
|UIViewAnimationOptionCurveEaseOut // 初めは速く終わりは遅くなるような変化
|UIViewAnimationOptionAllowUserInteraction // アニメーション中でもユーザによるViewの操作を可能にする
animations:^{
// このブロックの中にアニメーションの最終状態を記述する
self.alpha = 0.0; // alphaを0にする
self.bounds = CGRectMake(0, 0, 192, 192); // 波紋のサイズを192x192でframe全体に
}
completion:nil]; // アニメーションが終わっても何もしない
self.animating = YES;
こんな感じに1文で簡潔にアニメーションが記述できてしまう.今までの書き方と比べるとかなり良い.
さて,波紋のようにずっと繰り返す場合は options で UIViewAnimationOptionRepeat を指定する.
ここで嵌ったのが,これだけを指定してしまうとアニメーションが終わるまで UI 操作に制御が戻らなくなり,この場合,永遠に戻ってこなくなってしまう.アニメーション処理はどうも main thread で行われるみたいなので,ここの関数だけ別スレッドにしてもすぐに終了して,同じ状態になる.View のトランジションなどではなく,自分の位置を表示しながら他の操作ができないといけないので,これでは困る.
Cocoa の NSAnimationNonblocking みたいなものがないのかなーと blocking みたいなキーワードで探していたけど,見つからなかったのでもう一度 options を読み直したら見つかった.UIViewAnimationOptionAllowUserInteraction というオプション.これでアニメーション中でもユーザによる UI 操作が可能になる.
ちなみに初めは frame の値を変更するようなアニメーションを書いていたのだけど,これだとよく分からない動作が起きてしまう.frame は固定したまま描画領域だけを変えるのが安全だけど,この波紋をタップしたい,ってなったときにどうするんだろう...
以下ソースコード
/* MEMyself */ // 青い丸のマーカー @interface MEMyselfMarkerView : UIImageView { } @end // その周りの波紋 @interface MEMyselfRippleView : UIImageView { BOOL animating_; } @property BOOL animating; - (void)startRippling; @end // 自分自身の位置を表すクラス @interface MEMyself : MEUser { MEMyselfMarkerView *markerView_; MEMyselfRippleView *rippleView_; } @property (nonatomic, retain) MEMyselfMarkerView *markerView; @property (nonatomic, retain) MEMyselfRippleView *rippleView; @end
#import "MEMyself.h" @implementation MEMyselfMarkerView @end @implementation MEMyselfRippleView @synthesize animating=animating_; - (id)initWithImage:(UIImage *)image { self = [super initWithImage:image]; self.animating = NO; return self; } // 波紋のアニメーションを開始する - (void)startRippling { if(self.animating) return; // 初期状態の設定 // 最初は frame の中心に 0x0 のサイズで. self.bounds = CGRectMake(self.frame.size.width/2, self.frame.size.height/2, 0, 0); [UIView animateWithDuration:1.5f // 1.5秒置きに delay:0.0f // 0.0秒後から options:UIViewAnimationOptionRepeat // 永遠に繰り返す |UIViewAnimationOptionCurveEaseOut // 初めは早く終わりは遅くなるような変化 |UIViewAnimationOptionAllowUserInteraction // アニメーション中でもユーザによるViewの操作を可能にする animations:^{ // このブロックの中にアニメーションの最終状態を記述する self.alpha = 0.0; // alphaを0にする self.bounds = CGRectMake(0, 0, 192, 192); // 波紋のサイズを192x192でframe全体に } completion:nil]; // アニメーションが終わっても何もしない self.animating = YES; } @end @implementation MEMyself @synthesize markerView=markerView_; @synthesize rippleView=rippleView_; - (id)init { self = [super init]; self.screenCoord = CGPointMake(160, 240); self.frame = CGRectMake(self.screenCoord.x-16, self.screenCoord.y-16, 32, 32); self.backgroundColor = [UIColor clearColor]; self.markerView = [[MEMyselfMarkerView alloc] initWithImage:[UIImage imageNamed:@"BlueDot.png"]]; self.rippleView = [[MEMyselfRippleView alloc] initWithImage:[UIImage imageNamed:@"BlueDotRipple.png"]]; self.markerView.frame = CGRectMake(self.frame.size.width/2-12, self.frame.size.height/2-12, 24, 24); self.rippleView.frame = CGRectMake(self.frame.size.width/2-96, self.frame.size.height/2-96, 192, 192); [self addSubview:self.rippleView]; [self addSubview:self.markerView]; // ここではまだ superview が無いのでアニメーションを開始できない return self; } // マーカーがどこかのViewに追加されたらアニメーションを開始する - (void)didMoveToSuperview { [self.rippleView startRippling]; }
- 192 http://www.kagua.biz/twitter/ust-bgm.html
- 105 http://www.google.co.jp/search?sourceid=navclient&hl=ja&ie=UTF-8&rlz=1T4GGLL_jaJP309JP309&q=仮引数 配列 c
- 40 http://www.google.co.jp/search?hl=ja&source=hp&q=リモートファイルロック&aq=f&aqi=g6g-r4&aql=&oq=&gs_rfai=
- 37 http://www.google.co.jp/search?hl=ja&lr=lang_ja&tbs=lr:lang_1ja&q=youtube+windows+7+保存先+temporary&aq=f&aqi=&aql=&oq=&gs_rfai=
- 36 http://www.google.com/cse?cx=partner-pub-9300639326172081:d9bbzbtli15&ie=UTF-8&sa=Search&q=ubuntu+exeファイルを動かす方法&hl=ja
- 34 http://q.hatena.ne.jp/1254057925
- 32 http://www.google.com/custom?hl=ja&client=pub-9300639326172081&cof=FORID:13;AH:left;CX:Ubuntu%2010%2E04;L:http://www.google.com/intl/ja/images/logos/custom_search_logo_sm.gif;LH:30;LP:1;LC:#0000ff;VLC:#663399;DIV:#336699;&adkw=
- 29 http://d.hatena.ne.jp/limitusus/20080815/1218775782
- 27 http://iddy.jp/profile/yuyarin/
- 26 http://d.hatena.ne.jp/Hash/20080320/1206023787



