LoginSignup
10

More than 5 years have passed since last update.

CALayerでFlip Animation

Last updated at Posted at 2013-01-27

Dashboardにあるような裏返しアニメーションをCALayerを用いてやってみる。

単にアニメーションするだけならtransform.rotation.yの値をいじってやれば暗黙的なアニメーションを行うのだが、表と裏で違う情報を表示したい。そこで、CALayerのdoubleSidedにNOを指定すると、レイヤーが裏返しになったときに描画されなくなることを利用して、表面と裏面それぞれにレイヤーを用意し、同時に裏返すアニメーションをすることで実装する。

以下はARC下での実装例。テストはしてない。

@interface MyLayer : CALayer {
@private
    BOOL faceup_;

    CALayer *frontLayer_;
    CALayer *backLayer_;
}

@property BOOL faceup;
@end

@implementation MyLayer
@dynamic faceup;

- (BOOL)faceup {
    return faceup_;
}

- (void)setFaceup:(BOOL)faceup {
    if (faceup_ == faceup) return;
    faceup_ = faceup;

    // この設定値は試行錯誤の結果生まれました。
    CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
    anim.fromValue = @(M_PI);
    anim.byValue = (faceUp) ? @(M_PI) : @(-M_PI);   //表にするのと裏にするので回転方向を変える。
    anim.duration = .25;
    [frontLayer_ addAnimation:anim forKey:nil];
    [backLayer_ addAnimation:anim forKey:nil];

    // 新しいfaceupの値によってどちらかを表にする。
    [CATransaction setDisableActions:YES];
    CATransform3D t;
    t = CATransform3DMakeRotation((faceup) ? 0 : M_PI, 0, 1, 0);
    t.m34 = 1.0f / -420.0f;
    [frontLayer_ setTransform:t];
    t = CATransform3DMakeRotation((faceup) ? M_PI : 0, 0, 1, 0);
    t.m34 = 1.0f / -420.0f;
    [backLayer_ setTransform:t];
    [CATransaction setDisableActions:NO];
}

- (id)init {
    self = [super init];
    if (!self) return nil;

    frontLayer_ = [CALayer layer];
    backLayer_ = [CALayer layer];

    // 裏返し時に描画しない。
    frontLayer_.doubleSided = NO;
    backLayer_.doubleSided = NO;

    // あらかじめ裏返しておく。
    [backLayer_ setValue:@(M_PI) forKeyPath:@"transform.rotation.y"];

    [self addSublayer:frontLayer_];
    [self addSublayer:backLayer_];

    return self;
}

- (void)resizeSublayersWithOldSize:(CGSize)size {
    frontLayer_.frame = self.bounds;
    backLayer_.frame = self.bounds;
}

@end

frontLayerとbackLayerの公開範囲と描画方法は自由。


以下補足。(自分用メモ)
わざわざ明示的なアニメーションを使っているのは回転方向を指定するため。始めは暗黙的なアニメーションを使っていたのだが、frontLayerとbackLayerの回転方向が逆になってしまい、奇妙な動きをしてしまった。そこで明示的なアニメーションで過程を細かく制御し、暗黙的なアニメーションをオフにした。アニメーションのfromValueはfaceupの値で変えたり色々やったが、これで上手くいったのでこれにしておく。
CATransform3Dのm34の値は木下誠氏のLeopardのアニメーションを簡単実装! Core Animationを使いこなすを参考にした。
frontLayerとbackLayerを非公開にしたときに、-[CALayer hitTest:]でfrontLayerやbackLayerが返されるのが不都合な場合は、hitTest:を次の様にオーバーライドする。

- (CALayer *)hitTest:(CGPoint)p {
    CALayer *layer = [super hitTest:p];
    if (layer == frontLayer_ || layer == backLayer_) return self;
    else return layer;
}

何か不安な実装だが、これで動く。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10