もちろん、UIKitはオープンソースではなく、Appleで働いていないため、内部で何が起こっているのか正確にはわかりませんが、いくつかのアイデアがあります。
ブロックベースのUIView
アニメーションメソッドが導入される前は、アニメーションビューは次のように見えましたが、これらのメソッドは実際には引き続き使用できます。
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:duration];
myView.center = CGPointMake(300, 300);
[UIView commitAnimations];
これを知っていると、次のような独自のブロックベースのアニメーションメソッドを実装できます。
+ (void)my_animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations
{
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:duration];
animations();
[UIView commitAnimations];
}
animateWithDuration:animations:
...これは既存の方法とまったく同じように機能します。
UIView
ブロックを方程式から外すと、アニメーションブロック内で行われたときに、その(アニメーション化可能な)プロパティへの変更をアニメーション化するために使用する、ある種のグローバルアニメーション状態が必要であることが明らかになります。ネストされたアニメーションブロックを持つことができるため、これはある種のスタックである必要があります。
実際のアニメーションは、レイヤーレベルで機能するCore Animationによって実行されます。各アニメーションには、アニメーションと合成を担当UIView
するバッキングインスタンスがありますが、ビューは、ほとんどの場合、タッチイベントと座標系の変換を処理します。CALayer
ここでは、Core Animationの仕組みについては詳しく説明しません。そのために、 CoreAnimationプログラミングガイドを読むことをお勧めします。基本的に、これは、すべてのキーフレームを明示的に計算せずに、レイヤーツリーの変更をアニメーション化するシステムです(コアアニメーションから中間値を取得することは実際にはかなり困難です。通常、値、期間などを指定して、システムに許可します。詳細に注意してください)。
UIView
に基づいているためCALayer
、そのプロパティの多くは実際には基礎となるレイヤーに実装されています。たとえば、を設定または取得する場合view.center
、これはと同じでview.layer.location
あり、これらのいずれかを変更すると、もう一方も変更されます。
レイヤーは明示的にアニメーション化できます(これは、単純なものやより複雑なものなどCAAnimation
、いくつかの具体的な実装を持つ抽象クラスです)。CABasicAnimation
CAKeyframeAnimation
UIView
では、アニメーションブロック内で「魔法のように」アニメーション化する変更を実現するために、プロパティセッターは何を行うことができるでしょうか。それらの1つを再実装できるかどうかを見てみましょう。簡単にするために、を使用してみましょうsetCenter:
。
my_animateWithDuration:animations:
まず、グローバルを使用する上記のメソッドの修正バージョンを次に示します。これにより、アニメーションにかかる時間をメソッドCATransaction
で確認できます。setCenter:
- (void)my_animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations
{
[CATransaction begin];
[CATransaction setAnimationDuration:duration];
animations();
[CATransaction commit];
}
beginAnimations:...
を使用しなくなったことに注意してくださいcommitAnimations
。他に何もしなければ、何もアニメーション化されません。
それでは、サブクラスでオーバーライドsetCenter:
しましょう。UIView
@interface MyView : UIView
@end
@implementation MyView
- (void)setCenter:(CGPoint)position
{
if ([CATransaction animationDuration] > 0) {
CALayer *layer = self.layer;
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
animation.fromValue = [layer valueForKey:@"position"];
animation.toValue = [NSValue valueWithCGPoint:position];
layer.position = position;
[layer addAnimation:animation forKey:@"position"];
}
}
@end
ここでは、基になるレイヤーのプロパティをアニメーション化するCoreAnimationを使用して明示的なアニメーションを設定します。location
アニメーションの長さは、から自動的に取得されますCATransaction
。試してみましょう:
MyView *myView = [[MyView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
myView.backgroundColor = [UIColor redColor];
[self.view addSubview:myView];
[self my_animateWithDuration:4.0 animations:^{
NSLog(@"center before: %@", NSStringFromCGPoint(myView.center));
myView.center = CGPointMake(300, 300);
NSLog(@"center after : %@", NSStringFromCGPoint(myView.center));
}];
UIView
これがまさにアニメーションシステムの仕組みであると言っているのではなく、原則としてどのように機能するかを示すためだけのものです。