链接核心动画动画

38
哪种方法在Core Animation环境中链接动画是最优雅和模块化的?
我的意思是执行动画,只有当其他动画完成时才会启动(例如,更改位置,然后更改不透明度)...正常的方法是直接更改属性:
layer.position = new_point;
layer.opacity = 0.0f;

但是这会同时执行它们。我想让其中一个等待另一个。

还有关于不同对象的动画链怎么办?我曾经读到过像使用 CATransaction 这样的内容:

[CATransaction begin]
layer1.property = new_property;
[CATransaction begin]
layer2.property2 = new_property2;
[CATransaction commit];
[CATransaction commit];

但是似乎不起作用。

6个回答

80

您还可以使用动画分组并使用动画的beginTime字段。尝试像这样:

CABasicAnimation *posAnimation = [CABasicAnimation animationWithKeyPath:@"position"];
[posAnimation setFromValue:[NSNumber numberWithFloat:0.0]];
[posAnimation setToValue:[NSNumber numberWithFloat:1.0]];
// Here's the important part
[posAnimation setDuration:10.0];
[posAnimation setBeginTime:0.0];

CABasicAnimation *borderWidthAnimation = [CABasicAnimation animationWithKeyPath:@"borderWidth"];
[borderWidthAnimation setFromValue:[NSNumber numberWithFloat:0.0]];
[borderWidthAnimation setToValue:[NSNumber numberWithFloat:1.0]];
// Here's the important part
[borderWidthAnimation setDuration:10.0];
[borderWidthAnimation setBeginTime:5.0];

CAAnimationGroup *group = [CAAnimationGroup animation];
[group setDuration:10.0];
[group setAnimations:[NSArray arrayWithObjects:posAnimation, borderWidthAnimation, nil]];

[layer addAnimation:group forKey:nil];

需要注意整个动画的持续时间为10秒。第一个动画从第0秒开始,第二个动画从第5秒开始。


10
请注意,这很棒,但仅适用于应用于单个图层的动画。 - nielsbot
如果你想在第一个动画完成后立即调用一个方法,那么是不可能的。animationDidStop只有在整个组完成时才会被调用。 - durazno
关键路径“position”是CGPoint类型,需要使用NSValue对其进行编码(而不是NSNumber)。 - wcochran
将动画放在组内不会触发animationDidStop委托。总的来说,这是最简单的解决方案。 - Kwnstantinos Nikoloutsos

5
正如Matt所指出的那样,您可以创建动画组,这些动画组由具有不同开始时间的相同图层的不同动画组成。您还可以为独立的CAAnimation对象或CAAnimation组设置委托,并且每个动画完成时都会调用animationDidStop:finished:委托方法(请注意,作为组成部分的动画不会调用其委托的animationDidStop:finished:方法)。
我想出了一个很酷的技巧,使使用CAAnimation animationDidStop:finished:方法更加强大。我使用setValue:forKey:方法向单独的动画或动画组添加一段代码块,键为@"animationCompletionBlock"。然后,我编写了一个通用的animationDidStop:finished:方法,该方法检查刚刚完成的动画是否具有@"animationCompletionBlock"键,如果找到它,则在那里执行代码块。
请查看这个github项目,以获取该技术的工作示例:

带有完成块的CAAnimation演示

您也可以在一个组中设置一组动画。
[CATransaction begin];
//...
[[CATransaction commit];

像你建议的那样,使用block。这样做时,您可以使用CATransaction类方法setCompletionBlock:在当前事务组中的所有动画完成时调用代码块。一个事务的完成块可以触发下一个事务。


2

我使用setCompletionBlock方法定义一个闭包,当第一个动画完成时触发下一个动画:

[CATransaction begin]
layer1.property = new_property;
CATransaction.setCompletionBlock {
    [CATransaction begin]
    layer2.property2 = new_property2;
    [CATransaction commit];
}
[CATransaction commit];

0

我不相信你可以像你的例子中那样“嵌套”CA动画。

你需要为动画指定一个代理,并将第二个“转换”放在代理的animationDidStop:finished:选择器中。

可能需要查看苹果公司的动画类型和时间编程指南


0

我一直更喜欢的是不用分别设置每个动画的开始和结束时间:

我使用了A2DynamicDelegate(现在正在BlocksKit-Repo中开发,谁知道为什么 <_<),在CAAnimation的类别中实现了一个completionBlock属性。

这使我能够做到这样的事情:

CAAnimation *a = ...
CAAnimation *b = ...
CAAnimation *c = ...

a.completionHandler = ^{
    [self.layer addAnimation:b forKey:@"foo"];
    [self.layer addAnimation:c forKey:@"bar"];
};

更加灵活 :)
我已经上传了我的完成处理程序代码这里。不过请看一下头文件中的注意事项。我真的很困惑为什么这个方法没有被调用。

-1

不包括我“工具链”中的所有“技巧”,这个例子不能直接复制/粘贴...但它确实展示了一种非常简单的“链接”动画策略。

CATransform3D trans = m34();  // define chain-wide constants.
// Name your "stack". My "nextObject" returns arr[i]->done == nil. 
NSArray *layerStack = layer.sublayers;
//define a block, that "takes" a layer as it's argument.
void(^__block ChainBlock)(CALayer*) = ^(CALayer *m) { 
// animations, transforms, etc for each inividual "step".
    [m animate:@"transform" 
          // These are just NSValue-wrapped CAT3D's
          from:AZV3d(CATransform3DRotate(trans,  0,1,0,0)) 
            to:AZV3d(CATransform3DRotate(trans,1.5,1,0,0)) 
          time:2    // total time == each step * layerStack.count
         eased:kCAMediaTimingFunctionEaseOut
    completion:^{   // In completion, look for "next" layer.
                    CAL*  m2 = [layers nextObject]; 
// If there is "another" layer, call this block, again... with it.
                      if (m2)  chainAnis(m2);
// Otherise,you're done.  Cleanup, toggle values, whatevs.
                     else self.finishedProperty = YES;
    }];
};
// Give the block we just defined your "first" layer.
ChainBlock(layerStack[0]);  // It will recursively feed itself.

这显然取决于一些“外部魔法”,但概念很简单,并通过依赖关系消除了“处理”任何形式的粗略委托的需要。特别是,animate:from:to:time:easing:completion等类别来自伟大的Github上的FunSize Framework


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接