スレッド使ってCALayerでアニメーションする場合

最終的にこんな画面に。この一つ一つのマスがばらばらと表示されてくる感じのものを作る。

スレッド処理として以下のようなNSOperationのサブクラスを作る。CALayerを追加時にはopacityを0.0fにしておくので、アニメーションは0.8f秒かけて単純に透明度を1.0fにするだけ。いろいろとハードコーディングしてあるが、気にしないよう。

#import "TTView.h"
#import <QuartzCore/QuartzCore.h>
@interface TTOperation : NSOperation {
  CALayer *layer;
}
@property (retain) CALayer *layer;
@end

@implementation TTOperation
@synthesize layer;

-(void) main {
  [CATransaction begin];
  [CATransaction setValue:[NSNumber numberWithFloat:0.8f] forKey:kCATransactionAnimationDuration];
  layer.opacity = 1.0f;
  [CATransaction commit];
}

- (void)dealloc {
  self.layer = nil;
  [super dealloc];
}
@end

注意点としては、CALayerを親レイヤに追加する処理さえも暗黙のアニメーションになってしまうので、kCATransactionAnimationDurationの値を0.0fにしたトランザクション中でCALayerのプロパティを設定する。

こんなかんじ。

// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
    // Drawing code
  CGRect screenBounds = [UIScreen mainScreen].bounds;
  CGFloat rectLeft = 0.0f;
  CGFloat rectTop = screenBounds.size.height / 2 - screenBounds.size.width / 2;
  
  int m = 20;
  self.queue = [[[NSOperationQueue alloc] init] autorelease];

  // 処理をシャッフルする為に配列を使ってるだけ
  NSMutableArray *operations = [NSMutableArray array];

  TTOperation *operation;
  CGFloat unit = screenBounds.size.width / m;
  CALayer *layer;
  CGFloat red, green, blue, alpha;
  for (int i = 0; i < m; i++) {
    for (int j = 0; j < m; j++) {
      operation = [[TTOperation alloc] init];
      CALayer *layer = [CALayer layer];
      // ここのトランザクションでlayerへの設定を一気に行う
      [CATransaction begin];
      [CATransaction setValue:[NSNumber numberWithFloat:0.0f] forKey:kCATransactionAnimationDuration];

      layer.position = CGPointMake(rectLeft + unit * i + unit / 2, rectTop + unit * j + unit / 2);
      layer.bounds = CGRectMake(0, 0, screenBounds.size.width / m, screenBounds.size.width / m);
      red = (CGFloat)arc4random() / UINT_MAX;
      green = (CGFloat)arc4random() / UINT_MAX;
      blue = (CGFloat)arc4random() / UINT_MAX;
      alpha = (CGFloat)arc4random() / UINT_MAX;
      layer.backgroundColor = [[UIColor colorWithRed:red green:green blue:blue alpha:alpha] CGColor];
      operation.layer = layer;

      layer.opacity = 0.0f;
      [self.layer addSublayer:layer];
      [CATransaction commit];
      [operations addObject:operation];
      [operation release];
    }
  }
  
   // ランダムに取りだして、NSOperationQueueに追加する(シャッフル)
   while ([operations count] > 0) {
     int index = arc4random() % [operations count];
     id o = [operations objectAtIndex:index];
     [queue addOperation:o];
     [operations removeObjectAtIndex:index];
   }
}

ちなみに、arc4random という関数は、NSArrayをシャッフルする方法を検索した時に知った。FreeBSD由来のようだ。RC4アルゴリズムで品質の良い乱数を発生させてくれる。
[参考]


[追記]
waitUntilAllOperationsAreFinishedをすると、期待しているバラバラと表示されるアニメーションじゃなくなって、一気に表示されてしまう。アニメーションとの相性スレッドの依存関係の問題なのかな?

[追記](20100819)
アニメーションの実行時間は以下のようにも指定可能

[CATransaction setAnimationDuration:30 / (CGFloat)1000];

プロパティを設定することによるアニメーションを防ぐには以下の設定を行う方が良い

[CATransaction setDisableActions:YES];