当然,我不知道幕后到底发生了什么,因为 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 的工作原理,您可能需要阅读Core Animation Programming Guide来了解它。本质上,它是一个在层树中动画变化的系统,无需显式计算每个关键帧(实际上从核心动画中获取中间值相当困难,您通常只需指定 from 和 to 值、持续时间等,然后让系统注意细节)。
因为UIView
是基于 a 的CALayer
,所以它的很多属性其实都是在底层实现的。例如,当您设置或获取时view.center
,这view.layer.location
与更改其中任何一个都会更改另一个。
层可以显式地进行动画处理CAAnimation
(这是一个具有许多具体实现的抽象类,例如CABasicAnimation
简单的事物和CAKeyframeAnimation
更复杂的事物)。
那么UIView
属性设置器可以做些什么来完成动画块中的“神奇”动画更改呢?让我们看看是否可以重新实现其中之一,为简单起见,让我们使用setCenter:
.
my_animateWithDuration:animations:
首先,这是上面使用 global的方法的修改版本CATransaction
,以便我们可以在我们的方法中找出setCenter:
动画应该花费多长时间:
- (void)my_animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations
{
[CATransaction begin];
[CATransaction setAnimationDuration:duration];
animations();
[CATransaction commit];
}
请注意,我们不再使用beginAnimations:...
and 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
在这里,我们使用 Core Animation 设置了一个显式动画,它为底层的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
动画系统的工作方式,只是为了展示它在原则上是如何工作的。