在Layer-Backed视图上使用核心动画

时间:2014-01-20 05:11:35

标签: ios objective-c animation

Core Animation Programming guide中,有一个关于How to Animate Layer-Backed Views的段落,它说:

  

如果要使用Core Animation类来启动动画,则必须从基于视图的动画块中发出所有Core Animation调用。 UIView类默认禁用图层动画,但在动画块内重新启用它们。因此,您在动画块之外所做的任何更改都不会设置动画。

还有一个例子:

[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"];
}];

在我看来,它告诉我如果不从基于视图的动画块中发出核心动画调用,就不会有动画。

但似乎如果我直接添加核心动画调用而没有基于视图的动画块,它的工作方式是相同的。

我错过了什么吗?

3 个答案:

答案 0 :(得分:18)

tl; dr:该文档仅涉及隐式动画。显式动画在动画块之外工作正常。


我对文档的解释

来自文档的引用的简化版本就像(我解释它):

  

除了动画块之外,UIView已禁用隐式动画。如果你想做隐式图层动画,你必须在动画块中进行。

什么是隐式动画?它们是如何工作的?

隐式动画是独立图层的可动画属性发生变化时发生的情况。例如,如果您创建一个图层并更改它的位置,它将设置为新位置的动画。默认情况下,许多层属性都具有此行为。

它发生的事情是这样的:

  1. 系统启动交易(没有我们做任何事情)
  2. 属性的值已更改
  3. 图层查找该属性的操作
  4. 在某些时候交易已经提交(没有我们做任何事情)
  5. 找到的操作已应用
  6. 请注意,上面没有提及动画,而是有"动作"。此上下文中的操作是指实现CAAction协议的对象。它最有可能成为一些CAAnimation子类(如CABasicAnimation,CAKeyframeAnimation或CATransition),但它可以与符合该协议的任何内容一起使用。

    它如何知道"行动"采取?

    通过调用图层上的actionForKey:来查找该属性的操作。默认实现以此顺序查找操作:

    此搜索按此顺序进行(参考:actionForKey: documentation

      
        
    1. 如果图层具有委托并且该委托实现了访问图层的过滤器方法,则图层将调用该方法。代表必须执行以下操作之一:      
          
      • 返回给定密钥的操作对象。
      •   
      • 如果不处理此操作,请返回nil
      •   
      • 如果NSNull对象未处理该操作,则返回该对象,并且应终止搜索。
      •   
    2.   
    3. 图层在图层的actions字典中查找。
    4.   
    5. 该图层在style字典中查找包含密钥的操作字典。
    6.   
    7. 图层调用其defaultActionForKey:方法来查找任何类定义的操作。
    8.   
    9. 该图层查找Core Animation定义的任何隐式动作。
    10.   

    UIView在做什么?

    对于支持视图的图层,视图可以启用或禁用操作by implementing the delegate method actionForLayer:forKey。对于正常情况(在动画块之外),视图通过返回[NSNull null]来禁用隐式动画,这意味着:

      

    它不处理动作,应该终止搜索。

    但是,在动画块中,视图会返回实际动作。通过在动画块内部和外部手动调用actionForLayer:forKey:,可以轻松验证这一点。它也可能返回nil,这会导致图层继续寻找动作,如果在此之前找不到任何内容,最终会以隐式动作(如果有的话)结束。

    当找到某个操作并且提交了该事务时,该操作将使用常规addAnimation:forKey:机制添加到该层。通过创建自定义图层子类并在-actionForKey:-addAnimation:forKey:内部进行日志记录,然后在您覆盖+layerClass并返回自定义图层类的自定义视图子类中,可以轻松验证这一点。您将看到独立图层实例记录两种方法以进行常规属性更改,但背景层不会添加动画,除非在动画块中。

    为什么对隐式动画进行这么长的解释?

    现在,为什么我要对隐式动画如何工作给出这么长的解释?好吧,它表明他们使用与您自己使用相同的方法和明确的动画。知道它们是如何工作的,我们可以理解当文档说:" UIView类默认禁用图层动画但是在动画块中重新启用它们"

    UIView没有禁用显式动画的原因是你自己正在做所有工作:更改属性值,然后调用addAnimation:forKey:

    代码中的结果:

    动画块之外:

    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 :)
    

    动画块内:

    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 :)
    

    您可以在上面看到,独立图层上的显式动画和隐式动画会为动画块的外部和内部设置动画,但是图层支持的视图的隐式动画仅在动画块内部进行动画处理。

答案 1 :(得分:0)

我将通过一个简单的演示(例子)来解释它 在视图控制器中添加以下代码。(不要忘记导入 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方法中看到没有UIVIew动画块 当您运行应用程序并单击屏幕时,图层的不透明度会动画。这是因为默认情况下这些是可动画的。默认情况下,图层的所有属性都是可动画的。

现在考虑层支持视图的情况 将代码更改为

- (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);
}

您可能会认为这也会为其视图设置动画。但它不会发生。因为默认情况下图层支持的视图不可动画。
要使这些动画发生,您必须在UIView动画块中明确嵌入代码。如下所示,

-(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类(显然是图层支持的)默认情况下会禁用图层动画,但会在动画块中重新启用它们。因此,您在动画块之外所做的任何更改都不会设置动画。

答案 2 :(得分:0)

好吧,我从未在视图动画块中使用过显式的Core Animation。文件似乎根本不清楚。
可能意思就是这样,只是猜测:

  

如果要为链接到的视图的属性设置动画   您应该将它们包装到视图动画块中。在里面   如果您尝试更改图层不透明度,则查看图层   动画,但如果包装到视图动画块中则为。

在您的代码段中,您将直接创建基本动画,从而显式创建动画。可能文档只是想指出视图和图层之间的差异。在后者中,大多数属性的动画都是隐含的 如果你写这样的东西,你可以看到差异:

[UIView animateWithDuration:1.0 animations:^{
   // Change the opacity implicitly.
   myView.layer.opacity = 0.0;
}];

这将是动画。

   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"];

这将是动画。