如何UIViewanimation块的工作原理

我是新来的Objective-C / iOS编程,我想了解UIViewanimation如何工作。

说我有这样的代码:

[UIView animateWithDuration:2.0 animations:^{ self.label.alpha = 1.0; }]; 

作为animationsparameter passing的是Objective-C模块(类似于其他语言中的lambdas /匿名函数),然后将labelalpha属性从当前值更改为1.0。

但是,该块不接受animation进度参数(例如,从0.0到1.0或从0到1000)。 我的问题是animation框架如何使用这个块来了解中间帧,因为块只能指定最终状态。

编辑:我的问题是 animateWithDuration方法引擎盖操作而不是使用它方式

我对animateWithDuration代码如何工作的假设如下:

  1. animateWithDuration为所有的视图对象激活某种特殊的状态,其中的变化并不是真正被执行,而是只被注册。
  2. 它执行该块并且更改被注册。
  3. 它查询视图对象的状态变化并获取初始值和目标值,从而知道要更改哪些属性以及在哪些范围内执行更改。
  4. 它会根据持续时间和初始/目标值计算中间帧,然后触发animation。

有人可以对animateWithDuration是否真的有效吗?

当然,我不知道在底下发生了什么,因为UIKit不是开源的,我不在苹果公司工作,但这里有一些想法:

引入基于块的UIViewanimation方法之前,animation视图看起来像这样,这些方法实际上仍然可用:

 [UIView beginAnimations:nil context:nil]; [UIView setAnimationDuration:duration]; myView.center = CGPointMake(300, 300); [UIView commitAnimations]; 

知道这一点,我们可以实现我们自己的基于块的animation方法,如下所示:

 + (void)my_animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations { [UIView beginAnimations:nil context:nil]; [UIView setAnimationDuration:duration]; animations(); [UIView commitAnimations]; } 

…将与现有的animateWithDuration:animations: method。

从这个等式中,我们清楚地知道,必须有某种全局animation状态, UIView随后会在animation块中完成animation变化(animation)属性。 这必须是某种堆栈,因为你可以嵌套animation块。

实际的animation由核心animation执行,它在层级上工作 – 每个UIView有一个支持animation和合成的支持CALayer实例,而视图大多只处理触摸事件和坐标系统转换。

在这里我不会详细讨论Core Animation如何工作,你可能想阅读Core Animation Programming Guide 。 从本质上讲,这是一个系统来animation层次树中的变化,而不明确计算每个关键帧(实际上,从核心animation中获取中间值实际上相当困难,通常只是指定值和值,持续时间等,让系统照顾细节)。

因为UIView基于CALayer ,所以它的许多属性实际上是在底层实现的。 例如,当你设置或获取view.center ,这是相同的view.layer.location和改变其中任何一个也将改变其他。

可以使用CAAnimation (这是一个具有许多具体实现的抽象类,如简单的事情的CABasicAnimation和更复杂的东西的CAKeyframeAnimation显式地animation层。

那么一个UIView属性设置器可以做什么来实现animation块中的“神奇”animation变化? 让我们看看我们是否可以重新实现它们中的一个,为了简单起见,让我们使用setCenter:

首先,下面是使用全局CATransactionmy_animateWithDuration:animations:方法的修改版本,以便我们可以在setCenter:方法中findanimation应该运行多长时间:

 - (void)my_animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations { [CATransaction begin]; [CATransaction setAnimationDuration:duration]; animations(); [CATransaction commit]; } 

请注意,我们不再使用beginAnimations:...commitAnimations ,所以没有做任何事情,什么都不会animation。

现在,让我们重写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 

在这里,我们使用核心animation设置了一个显式的animation,animation化了底层的location属性。 animation的持续时间将自动从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)); }]; 

我并不是说这就是UIViewanimation系统的工作原理,只是为了说明如何在原理上工作。

没有指定中间帧的值。 在之前设置的值和在animation块内部设置的目的地值之间自动生成值的animation(在这种情况下是alpha,还有颜色,位置等)。 您可以通过使用animateWithDuration:delay:options:animations:completion:指定选项来影响曲线animateWithDuration:delay:options:animations:completion:默认是UIViewAnimationOptionCurveEaseInOut ,即值更改的速度将加速和减速)。

请注意,以前设置的值的animation更改将首先完成,即每个animation块指定从上一个结束值到新的animation。 您可以指定UIViewAnimationOptionBeginFromCurrentState从已经进行的animation的状态开始新的animation。

这会将块内指定的属性从当前值更改为您提供的任何值,并在整个持续时间内线性地执行。

所以如果原始的alpha值是0,这会在2秒内褪色标签。 如果原始值已经是1.0,则根本看不到任何效果。

在引擎盖下, UIView负责确定需要进行更改的animation帧数量。

您还可以通过将缓动曲线指定为UIViewAnimationOption来更改发生更改的速率。 再一次, UIView为你处理补间。