CALayerのアニメーションを読みやすくカスタムしてみた。

いやぁ、iPhone 5s と 5cが発表されましたね〜。
それはそれとしてCALayerのアニメーションを書きやすく/読みやすくするPBReadableTransactionを公開しました。

CALayerをアニメーションさせる場合には、CAAnimationを使ういわゆる明示的アニメーション暗黙的アニメーションがあります。
前者は [layer addAnimation:animation forKey:key]; といった感じで書くヤツですね。
後者は layer.opacity = 0.4f; のように、CALayerのAnimatableなプロパティに変更を加えると勝手にアニメーションしてくれるヤツ。楽チン!

で、今回は後者の暗黙的アニメーション/CATransactionについて。

複数のレイヤーを同時にアニメーションしたい場合や、同時に複数のプロパティを動かしたい場合には、CATransactionを使うんですがこれがどうしても読みづらい。
コードを書いている時には考えて書いているのでさほど問題は無いんですが、あとあとになって解読しようとすると個々のレイヤーがどうアニメーションしているのかがすぐに分からない。。なんて事が度々起こってしまうワケです。

これをもっと読みやすく/書きやすく出来ないか?と考えたのが今回のキッカケ。

例えば、
layerAをフェードアウトしつつ45度回転。
45度回転させてあったlayerBを元に戻しながらフェードイン。
アニメーションが終わったらlayerCを拡大しながらフェードアウト + layerAをhidden。
といった処理をしたい場合には、通常は以下のように書きますね。


[CATransaction begin];
[CATransaction setAnimationDuration:duration];
[CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];

// After Animation.
[CATransaction setCompletionBlock:^{
    layerC.affineTransform = CGAffineTransformMakeScale(1.5f, 1.5f);     // Scale 1.5
    layerA.hidden = YES;
}];

// animation.
layerA.opacity = 0.0f;
layerA.affineTransform = CGAffineTransformMakeRotation(M_PI * 0.5f);    // Rotate 45.
layerB.opacity = 1.0f;
layerB.affineTransform = CGAffineTransformIdentity;                      // Set Default.

[CATransaction commit];

どうでしょう?
かなり慣れた人でないと、これをパッと見てすぐに頭にアニメーションを描くのは難しいんじゃないかなぁ?と。

さらに、animAが終わったらanimBしてanimCして終わったら完了処理…なんて場合には、setCompletionBlockをアニメーションの前に書く仕様のせいもあってホントナニヤッテンダカワケワカラン状態になること受け合いです(^〜^;)
(setCompletionBlockを後ろに書くと意図しない挙動になってしまうので。)

んじゃぁ、どう書ければ分かりやすくなるか?って事で今回実現したのが以下の形。


[CATransaction animateWithDuration:duration
                animationCurveType:PBCurveTypeEaseInOut
                        animations:^{
                            layerA.basicAnims = @{kFade: @(0.0f), kRotate: @(90.0f)};
                            layerB.basicAnims = @{kFade: @(1.0f), kRotate: @(0.0f)};
                        } completion:^{
                            layerC.scale = 1.5f;
                            layerA.hidden = YES;
                        }];

どうです?
ダイブ読みやすくなったんではないでしょうか?

CATransactionのメソッド名自体はUIViewのアニメーションを踏襲しているので説明は不要かな、と。
一カ所 animationCurveType: という引数ラベルがありますが、これは長々しいCAMediaTimingFunctionをシンプルに指定出来るようにしただけ。
PBCurveTypeEaseInOut PBCurveTypeEaseIn といった感じでイージングが指定出来ます。

もちろん、アニメーションさせずにプロパティを切り替えたい時のためのメソッドも用意しました。


[CATransaction nonAnimateBlock:^{
    // レイヤーにプロパティをセット。
}];

簡易アニメーションプロパティ

Githubにも書いてありますが、レイヤーに今回用意した簡易アニメーションプロパティをセットする方法を書いておきます。

まずは、ひとつのプロパティだけアニメーションさせたい場合はこんな風に書きます。


// CALayer
layer.fade = 0.20f;     // 透過20%に。 (layer.opacity)
layer.rotate = 45.0f;   // 45度回転 (CGAffineTransformMakeRotation(...))
layer.scale = 1.3f      // 1.3倍に拡大 (CGAffineTransformMakeScale(...))
// 上右10px移動。(CGAffineTransformMakeTranslate(...))
layer.move = (UIOffset){10.0f, 10.0f};

// CAShapeLayer専用
shapeLayer.drawOutline = 1.0f;  // 外形に沿って線を引く (shapeLayer.strokeEnd)

ちなみに layer.move = (UIOffset){10.0f, 10.0f}; とありますが、これはいわゆる『今どきのモダンな書き方』ってヤツで、 NSArray *array = @{objectA, objectC}; とかのノリで (UIOffset){10.0f, 10.0f}; UIOffsetMake(10.0f, 10.0f); と書くのと同じ意味になります。

次にひとつのレイヤーに複数のプロパティを入れる場合はこちら。


// フェード、回転、拡大
layer.basicAnims = @{kFade: @0.0f, kRotate: @90.0f, kScale: @1.2f};

// フェード、移動、シェイプ外形の描画
shapeLayer.basicAnims = @{kFade: @0.4f, kMove: moveOffset(10.0f, -15.0f), kDrawOutline: 0.0f};

moveOffset(10.0f, -15.0f) とありますが、これはUIOffsetの値をNSValueに変換する関数です。basicAnimsに入れる場合はこれを使ってください。

セットしたプロパティを一括でデフォルト値に戻す場合はこちら。


// すべてのプロパティをデフォルト値に。
[layer resetBasicAnims];

あくまで簡易的なアニメーション用なので、フェード/回転/拡大縮小/移動/strokeEndしか実装していませんのであしからず。
より綿密なアニメーションにはCAAnimationを使う。ってことで。

(ちなみにCATransactionの仕様上、回転は180度以下のみです。)

より複雑なアニメーションも読みやすく

CAAnimationに比べて複数の同時アニメーションをスッキリと書けるCATransactionですが、今回のカスタムによって、より複雑なアニメーションも分かりやすく書くことが出来ます。

例えば、タップによってAからB、BからAにトグル(ON/OFF)するボタンにアニメーションを付ける場合は、ONになる場合とOFFになる場合とでアニメーションが違ったりするので、コードも複雑になりがちです。

そんな場合でもこんな感じで書けば読みやすいですよ。


typedef void (^transactionBlock_t)();

- (void)beginToggleAnimationIsOn:(BOOL)isOn
{
    transactionBlock_t readyBlock = nil;
    transactionBlock_t firstAnimBlock = nil;
    transactionBlock_t secondAnimBlock = nil;
    transactionBlock_t completeBlock = nil;

    // ON, OFFそれぞれのアニメーションをまとめてここに書く。
    if (isOn) {
        readyBlock = ^ {
            // アニメーション前のセッティング
        };
        firstAnimBlock = ^{ ... };
        secondAnimBlock = ^{ ... };
        completeBlock = ^{ ... };
    } else {
        readyBlock = ...
        firstAnimBlock = ...
        secondAnimBlock = ...
        completeBlock = ...
    }

    [CATransaction nonAnimateBlock:readyBlock];             // ready

    [CATransaction animateWithDuration:firstDuration
                animationCurveType:PBCurveTypeEaseInOut
                        animations:firstAnimBlock           // first animation
                        completion:^{
        [CATransaction animateWithDuration:secondDuration
                        animationCurveType:PBCurveTypeEaseOut
                                animations:secondAnimBlock  // second animation
                                completion:completeBlock];  // completion
    }];
}

。。。と、長々とした説明になってしまいましたが、PBReadableTransaction.h をインポートして以上のような形で書けば良いだけなので簡単です。

『便利そうだな!』と思ったら以下のリンクからご自由に使ってくださいませ<(_ _)>

でわ!

[ソースリンク]: Jacminik/PBReadableTransaction

広告

CALayerのアニメーションを読みやすくカスタムしてみた。」への1件のフィードバック

  1. ピンバック: 【おはあぷ】iPhone 5s,5cの話題満載でございます。 | あぷまがどっとねっと

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中