如何UIViewanimation块的工作原理
我是新来的Objective-C / iOS编程,我想了解UIViewanimation如何工作。
说我有这样的代码:
[UIView animateWithDuration:2.0 animations:^{ self.label.alpha = 1.0; }];
作为animations
parameter passing的是Objective-C模块(类似于其他语言中的lambdas /匿名函数),然后将label
的alpha
属性从当前值更改为1.0。
但是,该块不接受animation进度参数(例如,从0.0到1.0或从0到1000)。 我的问题是animation框架如何使用这个块来了解中间帧,因为块只能指定最终状态。
编辑:我的问题是在 animateWithDuration
方法引擎盖操作而不是使用它的方式 。
我对animateWithDuration
代码如何工作的假设如下:
-
animateWithDuration
为所有的视图对象激活某种特殊的状态,其中的变化并不是真正被执行,而是只被注册。 - 它执行该块并且更改被注册。
- 它查询视图对象的状态变化并获取初始值和目标值,从而知道要更改哪些属性以及在哪些范围内执行更改。
- 它会根据持续时间和初始/目标值计算中间帧,然后触发animation。
有人可以对animateWithDuration
是否真的有效吗?
当然,我不知道在底下发生了什么,因为UIKit不是开源的,我不在苹果公司工作,但这里有一些想法:
引入基于块的UIView
animation方法之前,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:
首先,下面是使用全局CATransaction
的my_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)); }];
我并不是说这就是UIView
animation系统的工作原理,只是为了说明它如何在原理上工作。
没有指定中间帧的值。 在之前设置的值和在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
为你处理补间。