在图层背景视图上使用核心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到新的位置。 很多很多图层属性默认都有这种行为。

发生这样的事情:

  1. 系统启动交易(没有我们做任何事情)
  2. 一个属性的值被改变
  3. 该层将查找该属性的操作
  4. 在某个时候交易是承诺的(没有我们做任何事情)
  5. 被发现的行为被应用

请注意,上面没有提及animation,而是有“动作”一词。 在此上下文中的动作是指实现CAAction协议的对象。 这很可能是一些CAAnimation的子类(比如CABasicAnimation,CAKeyframeAnimation或者CATransition),但是它可以与任何符合该协议的东西一起工作。

它如何知道要采取什么“行动”?

通过在图层上调用actionForKey:来查找该属性的动作。 这个默认实现按这个顺序查找一个操作:

这个search按照这个顺序(ref: actionForKey: documentation )

  1. 如果图层具有委托,并且该委托实现了“访问图层的filter”方法,则图层会调用该方法。 代表必须执行以下任一操作:
    • 返回给定键的操作对象。
    • 如果不处理该操作,则返回nil
    • 如果不处理该操作,则返回NSNull对象,并且应该终止search。
  2. 该图层在图层的actions字典中查找。
  3. 该图层在style字典中查找包含该键的动作字典。
  4. 该层调用其defaultActionForKey:方法来查找任何类定义的操作。
  5. 该图层查找由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。