2011年10月26日水曜日

Blocksを使ってUIViewアニメーション

Children's Blocks by lobo235
Children's Blocks, a photo by lobo235 on Flickr.
iOS 4.0 から Blocks が使えるようになって、UIView アニメーションにも Blocks が使えるようになりました。
UIView アニメーションにBlocks が使えると、とても便利なのですが、iOS 3 デバイスをサポートする関係上、なかなか使用する機会がありませんでした。
今回初めて使用してみたので、その使い方を書いてみたいと思います。

Blocks の詳細については、ドキュメントが日本語化されているので、そちらをご覧下さい。

UIView アニメーション Blocks 以前
複数のアニメーションを直列につなげて実行させたい場合、beginAnimations:context: と commitAnimations メソッドを使用すると、以下のように一つのアニメーション毎にメソッドを用意してやったりします。これだと見通しが悪いし、繋げるアニメーションが多いと絶望的に面倒くさいです。
// 一番最初のアニメーション開始場所
- (void)startAnimation
{
  someView.frame = CGRectMake(0,-40,480,40);

  CGContextRef ctx = UIGraphicsGetCurrentContext();
  [UIView beginAnimations:nil context:ctx];
  [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
  [UIView setAnimationDuration:0.5];
  [UIView setAnimationDelegate:self];
  [UIView setAnimationDidStopSelector:@selector(endAnimation1)]; // 次のアニメーション開始メソッド

  someView.frame = CGRectMake(0,0,480,40);
 
  [UIView commitAnimations];
}

// 前のアニメーションの終わりに呼ばれるメソッド
// ここが次のアニメーションの開始場所
- (void)endAnimation1
{
  CGContextRef ctx = UIGraphicsGetCurrentContext();
  [UIView beginAnimations:nil context:ctx];
  [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
  [UIView setAnimationDuration:3.0];
  [UIView setAnimationDelegate:self];
  [UIView setAnimationDidStopSelector:@selector(endAnimation2)];

  someView.frame = CGRectMake(0,1,480,40);

  [UIView commitAnimations];
}
// ・・・以下続く

UIView アニメーション Blocks 以後
対して、animateWithDuration:animations:completion: メソッドだとどうでしょうか?
+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion
animations: の後にアニメーション処理をブロックで、completion: の後にアニメーション後の処理をブロックで指定します。
複数のアニメーションを直列につなげる場合は、以下のようにします。
UIView *someView = ビューをインスタンス化してローカル変数に退避;
someView.frame = CGRectMake(0,-40,480,40);

[self.view addSubview:someView];

[UIView animateWithDuration:0.5
     animations:^{someView.frame = CGRectMake(0,0,480,40);}
     completion:^(BOOL finished) {
[UIView animateWithDuration:3.0
     animations:^{someView.frame = CGRectMake(0,1,480,40);}
     completion:^(BOOL finished) {
[UIView animateWithDuration:0.5
     animations:^{someView.frame = CGRectMake(0,-40,480,40);}
     completion:^(BOOL finished) {
[someView removeFromSuperview];
}];  }];  }];
ずいぶん見通しが良くなりました。
まあ、最後の括弧が見ため悪いですが、一連のアニメーションを一つのメソッド内で記述できてしまうのは楽です。

さらに特筆すべきは、メソッド内のローカル変数(someView)にブロック内からもアクセスできるという点です。
詳しい仕組みは Blocks のドキュメントに譲りますが、大雑把に言うとブロック内からアクセスするローカル変数(スタック上にある変数)はコピーされて保存され、そのブロックが生きている限り保持されるようです。更にヒープ領域にあるオブジェクトには、retain して保持するようです。(ブロックの消滅とともに release するもよう)

最後に、上記2つ目アニメーションで、Y座標を1だけ動かすのに3秒を割り当てているのは、ウエイトの代わりです。ここを仮に CGRectMake(0,0,480,40)のままにしたり、何も命令を記述しないと、3秒を待たず直ぐに completion ブロックが起動されてしまいます。

0 件のコメント:

コメントを投稿

Related Posts Plugin for WordPress, Blogger...