在图层背景视图上使用核心animation
在“ 核心animation编程指南”中 ,有一段关于How to Animate Layer-Backed Views
,它说:
如果要使用Core Animation类来启动animation,则必须从基于视图的animation块中发出所有Core Animation调用。 UIView类默认禁用图层animation,但在animation块中重新启用它们。 因此,您在animation块之外进行的任何更改都不会生成animation。
也有一个例子:
[UIView animateWithDuration:1.0 animations:^{ // Change the opacity implicitly. myView.layer.opacity = 0.0; // Change the position explicitly. CABasicAnimation* theAnim = [CABasicAnimation animationWithKeyPath:@"position"]; theAnim.fromValue = [NSValue valueWithCGPoint:myView.layer.position]; theAnim.toValue = [NSValue valueWithCGPoint:myNewPosition]; theAnim.duration = 3.0; [myView.layer addAnimation:theAnim forKey:@"AnimateFrame"]; }];
在我看来,它告诉我如果不从基于视图的animation块内发出核心animation调用,就不会有animation。
但是,如果我直接添加核心animation调用,而没有基于视图的animation块,它的工作原理是一样的。
我错过了什么吗?
tl; dr:文档仅涉及隐式animation。 显式animation在animation块之外很好地工作。
我对文件的解释
从文档中引用的简化版本是类似的东西(我换句话说):
UIView禁用了隐含的animation,除了在animation块内。 如果你想要做隐式层animation,你必须在一个animation块中做它们。
什么是隐式animation,它们是如何工作的?
隐式animation是独立图层的animation属性更改时发生的情况。 例如,如果您创build一个图层并更改它的位置,它将animation到新的位置。 很多很多图层属性默认都有这种行为。
发生这样的事情:
- 系统启动交易(没有我们做任何事情)
- 一个属性的值被改变
- 该层将查找该属性的操作
- 在某个时候交易是承诺的(没有我们做任何事情)
- 被发现的行为被应用
请注意,上面没有提及animation,而是有“动作”一词。 在此上下文中的动作是指实现CAAction
协议的对象。 这很可能是一些CAAnimation的子类(比如CABasicAnimation,CAKeyframeAnimation或者CATransition),但是它可以与任何符合该协议的东西一起工作。
它如何知道要采取什么“行动”?
通过在图层上调用actionForKey:
来查找该属性的动作。 这个默认实现按这个顺序查找一个操作:
这个search按照这个顺序(ref: actionForKey:
documentation )
- 如果图层具有委托,并且该委托实现了“访问图层的filter”方法,则图层会调用该方法。 代表必须执行以下任一操作:
- 返回给定键的操作对象。
- 如果不处理该操作,则返回
nil
。- 如果不处理该操作,则返回
NSNull
对象,并且应该终止search。- 该图层在图层的
actions
字典中查找。- 该图层在
style
字典中查找包含该键的动作字典。- 该层调用其
defaultActionForKey:
方法来查找任何类定义的操作。- 该图层查找由Core Animation定义的任何隐式操作。
什么是UIView在做什么?
对于支持视图的图层,视图可以通过实现委托方法actionForLayer:forKey
来启用或禁用动作。 对于正常情况(在animation块之外),视图通过返回[NSNull null]
来禁用隐式animation,这意味着:
它不处理该操作,search应该被终止。
但是,在animation块内部,视图返回一个真实的动作。 这可以通过手动调用actionForLayer:forKey:
在animation块内部和外部来轻松validation。 它也可能返回nil
,这将导致层继续寻找一个行动,最终结束于隐式行动(如果有的话),如果它之前找不到任何东西。
当find一个动作并且提交这个事务时,这个动作会使用常规的addAnimation:forKey:
机制添加到这个图层中。 这可以通过创build自定义图层子类并在-actionForKey:
和-addAnimation:forKey:
,然后在覆盖+layerClass
并返回自定义图层类的自定义视图子类中轻松validation。 您将看到,独立层实例logging了常规属性更改的两种方法,但是背景层不会添加animation,除非在animation块中。
为什么这个隐式animation的长解释?
现在,为什么我对这个隐式animation的工作原理做了这么长时间的解释呢? 那么,就是要表明他们使用了与显式animation相同的方法。 了解它们是如何工作的,我们可以理解当文档说:“UIView类默认禁用层animation,但在animation块中重新启用它们”。
显式animation不被UIView禁用的原因是你自己正在做所有的工作:改变属性值,然后调用addAnimation:forKey:
代码中的结果:
animation块之外:
myView.backgroundColor = [UIColor redColor]; // will not animate :( myLayer.backGroundColor = [[UIColor redColor] CGColor]; // animates :) myView.layer.backGroundColor = [[UIColor redColor] CGColor]; // will not animate :( [myView.layer addAnimation:myAnimation forKey:@"myKey"]; // animates :)
animation块内部:
myView.backgroundColor = [UIColor redColor]; // animates :) myLayer.backGroundColor = [[UIColor redColor] CGColor]; // animates :) myView.layer.backGroundColor = [[UIColor redColor] CGColor]; // animates :) [myView.layer addAnimation:myAnimation forKey:@"myKey"]; // animates :)
您可以在上面看到,独立图层上的显式animation和隐式animation在animation块的外部和内部都进行了animation处理,但是,支持层的视图的隐式animation只会在animation块内animation。
我将用一个简单的演示来解释它(例子)。
在你的视图控制器中添加下面的代码(不要忘记导入QuartzCore )
@implementation ViewController{ UIView *view; CALayer *layer; } - (void)viewDidLoad { [super viewDidLoad]; view =[[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; [self.view addSubview:view]; view.backgroundColor =[UIColor greenColor]; layer = [CALayer layer]; layer.frame =CGRectMake(0, 0, 50, 50); layer.backgroundColor =[UIColor redColor].CGColor; [view.layer addSublayer:layer]; } -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ layer.frame =CGRectMake(70, 70, 50, 50); }
在toucesBegan方法中看到没有UIVIewanimation块。
当你运行应用程序并单击屏幕时,图层的不透明度变成animation。这是因为默认情况下这些是animation的。默认情况下,图层的所有属性都是可以animation的。
现在考虑层支持视图的情况。
将代码更改为以下
- (void)viewDidLoad { [super viewDidLoad]; view =[[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; [self.view addSubview:view]; view.backgroundColor =[UIColor greenColor]; // layer = [CALayer layer]; // layer.frame =CGRectMake(0, 0, 50, 50); // layer.backgroundColor =[UIColor redColor].CGColor; // [view.layer addSublayer:layer]; } -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ view.layer.opacity =0.0; // layer.frame =CGRectMake(70, 70, 50, 50); }
您可能会认为这也会激发它的视图。但是它不会发生。因为默认情况下支持层的视图不是可以animation的。
为了使这些animation发生,你必须明确地将代码embedded到UIViewanimation块中。如下所示,
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ [UIView animateWithDuration:2.0 animations:^{ view.layer.opacity =0.0; }]; // layer.frame =CGRectMake(70, 70, 50, 50); }
这就解释了,
UIView类(显然是层支持的)在默认情况下禁用层animation,但在animation块中重新启用它们。因此,您在animation块外进行的任何更改都不会生成animation。
那么,我从来没有在视图animation块中使用明确的核心animation。 这个文件似乎根本不清楚。
可能这个意思就是这样,这只是一个猜测:
如果要为链接到支持图层的视图设置animation属性,则应将其包装到视图animation块中。 在视图的图层中,如果您尝试更改图层不透明度,则不会生成animation,但是如果将其封装到视图animation块中,
在你的代码片段中,你直接创build一个基本的animation,从而明确地创build一个animation。 文件可能只是想指出意见和层次之间的差异。 后者对大多数属性的animation是隐含的。
如果你写这样的话,你可以看到不同之处:
[UIView animateWithDuration:1.0 animations:^{ // Change the opacity implicitly. myView.layer.opacity = 0.0; }];
这将是animation。
myView.layer.opacity = 0.0;
这不会animation。
// Change the position explicitly. CABasicAnimation* theAnim = [CABasicAnimation animationWithKeyPath:@"position"]; theAnim.fromValue = [NSValue valueWithCGPoint:myView.layer.position]; theAnim.toValue = [NSValue valueWithCGPoint:myNewPosition]; theAnim.duration = 3.0; [myView.layer addAnimation:theAnim forKey:@"AnimateFrame"];
这将是animation。