2011年4月6日水曜日

CALayerを使って独自のプロパティをアニメーションさせる


元ネタはこちら

OpenGL ESのシーン管理でカメラやら照明やらモデルやらをアニメーションするのに使えないかなと。
あと、角度をプロパティで定義して時計の針を表示したりとかにも使えますね。

[簡単な手順] (簡単すぎてすいません)

- CALayerを継承して独自のプロパティを追加したクラスの作成 -
1) CALayerを継承したクラスを作成する(例としてクラス名をCustomLayerとします)

2) 独自のプロパティを定義する

3) drawInContext:をオーバーライドし、独自のプロパティに従った描画を行うロジックを記述

4) needsDisplayForKey:をオーバーライドし、独自のプロパティに対してYESを返すようにしてあげる。独自のプロパティ以外はsuperにお任せする

5) initWithLayer:をオーバーライドし、アニメーションさせるプロパティをコピーするようにしてあげる
(これはアニメーションする際にレイヤーをコピーする?処理が含まれるため必要)

@implementation CustomLayer
@synthesize hoge;

- (id) initWithLayer:(id)layer {
    NSLog(@"initWithLayer called.");
    if((self = [super initWithLayer:layer])) {
        if([layer isKindOfClass:[CustomLayer class]]) {
            CustomLayer *other = (CustomLayer*)layer;
                self.hoge = other.hoge;
        }
    }
    return self;
}

- (void)drawInContext:(CGContextRef)ctx
{
    // self.hogeの内容が変わりながら呼び出される
}

+ (BOOL)needsDisplayForKey:(NSString *)key {
    if ([key isEqualToString:@"hoge"]) {
        return YES;
    }
    else {
       return [super needsDisplayForKey:key];
    }
}
@end

- UIViewに作成したレイヤーを登録する -
1) 適当なタイミングでレイヤーオブジェクトを生成し、子レイヤーとして登録する
UIViewControllerのviewDidLoadでやってみる場合
- (void)viewDidLoad
{
    [super viewDidLoad];
    customLayer = [[CustomLayer alloc] init];
    customLayer.bounds = self.view.bounds;
    [self.view.layer addSublayer:customLayer];
}

2) アニメーションの定義
- (void)animateHoge{
 CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"hoge"];
 anim.duration = 3.0;
 anim.fromValue = [NSNumber numberWithDouble:50.0];
 anim.toValue = [NSNumber numberWithDouble:150.0];
 anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    
        // これでアニメーションが開始される
 [customLayer addAnimation:anim forKey:@"animateHoge"];
    
 customLayer.hoge = 150.0;
}

3) animateHogeを呼んでみると、CustomLayerのdisplayが自動でぶわーっと呼び出され、
displayからdisplayContext:が呼び出され、アニメーションしながら表示されるという寸法ですね。

こんな感じで、カメラ/照明/モデルの回転とか移動とかをアニメーションしながらシーン描画できたらなあと。あ、もちろん表示の更新はCADisplayLink使ってやるつもりですが。んーなんかちょっと無理あるか?