UIView の Animation Block の中で行われている事
Animation Block
iOS 4 から UIView のアニメーションを簡単にするために、以下のアニメーションに関するクラスメソッドが UIView に追加されました。
iOS 開発に携わってる人ならみんな知ってると思います。
+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations; + (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion; + (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion;
上記以外にも iOS 7 からは以下のようなキーフレームや UI Dynamics を用いたアニメーションのためのメソッドも追加されています。
+ (void)addKeyframeWithRelativeStartTime:(double)frameStartTime relativeDuration:(double)frameDuration animations:(void (^)(void))animations; + (void)animateKeyframesWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewKeyframeAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion + (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay usingSpringWithDamping:(CGFloat)dampingRatio initialSpringVelocity:(CGFloat)velocity options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion
これらのメソッドのおかげで、とても簡潔に UIView
をアニメーションさせることができるようになりました。
しかし、実際にあの Block の中ではどのような処理が走ってアニメーションが実行されているかが上手に隠蔽され、
あれだけで済んでしまったことによる Core Animation に対する認識不足が自分の中で目立ってきたので今回は調べてみました。
CAAnimation による明示的アニメーション
上記の UIView の簡易 API ができる以前はどのようにアニメーションを実装していたのか、
というそもそもの所に戻ってみるのが近道っぽいです。
以下のコードは Core Animation Programming Guide - Animating Layer Content の章にあるサンプルです。
CABasicAnimation* fadeAnim = [CABasicAnimation animationWithKeyPath:@"opacity"]; fadeAnim.fromValue = [NSNumber numberWithFloat:1.0]; fadeAnim.toValue = [NSNumber numberWithFloat:0.0]; fadeAnim.duration = 1.0; [theLayer addAnimation:fadeAnim forKey:@"opacity"]; // Change the actual data value in the layer to the final value. theLayer.opacity = 0.0;
CAAnimation
を作成し、それを CALayer
に addAnimation:forKey:
することでアニメーションを実行しています。
Animatable Property
少し脇道にそれますが、CALayer
には animatable property というのがあります。以下をみるとわかりますが、ほとんどがそれです。
Core Animation Programming Guide - Animatable Properties
では animatable property とは何なのでしょうか?
animatable property とはその名の通りアニメーション可能な値のことで、
その値を変更すると actionForKey:
が発火して、然るべき CAAction プロトコルに準拠したオブジェクトを返し、暗黙的アニメーションを実行するというプロパティです。
actionForKey:
が CAAction プロトコルに準拠したオブジェクトを返す流れは reference を見るのがわかりやすいです。
特別な定義が無ければ、animatable property と一緒に定義された CAAnimation
が返ります。
ということで、CAAnimation
による明示的アニメーションと animatable property が行う暗黙的アニメーション
について分かったところで UIView
の話に戻ります。
結論
上記でも書きましたが、スタンドアローンの CALayer
の animatable property が変更された場合は、CAAnimation
が発行されて暗黙的アニメーションが実行されます。
しかし UIView
が保持している CALayer に同じことをしても何も起こりません。ただ新しいフレームにアニメーション無しで移るだけです。
なぜかというと UIView
が自身が保持している layer のアニメーション機能をオフにしてるからなんですね。
これは Core Animation Programming Guide - How to Animate Layer-Backed Views にも書かれています。
The UIView class disables layer animations by default but reenables them inside animation blocks.
理由としては Core Text などと同様、 iPhone OS リリース当時のパフォーマンスが芳しくなかったからとかだと想像します。
どうやってオフにしてるかというと、CALayer
が CALayerDelegate
経由で UIView
に CAAnimation
を問い合わせる時に行っています。
で、それを簡単に有効にするために登場したのが、冒頭に書いた UIView Animation Block というわけですね。
上記の事柄は、以下のコードをみると一目瞭然だと思います。
NSLog(@“Animation Block 外: %@", [view actionForLayer:view.layer forKey:@"position"]); [UIView animateWithDuration:1.0 animations:^{ NSLog(@“Animation Block 内: %@", [view actionForLayer:view.layer forKey:@"position"]); }]; — 実行結果 Animation Block 外: <null> Animation Block 内: <CABasicAnimation: 0x8c2ff10>
まとめると、
UIView Animation Block の中で animatable property を変更した場合のみ、CAAnimation
が発行され暗黙的アニメーションが実行されている
ということでした。長々と失礼しました。