在核心动画上下文中链接动画的最优雅和模块化方式是什么?
我的意思是做动画,当其他完成时开始(例如,更改position
然后opacity
)。正常方法是直接更改属性:
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];
但它似乎不起作用..
答案 0 :(得分:76)
您还可以使用动画分组并使用动画的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个开始。
答案 1 :(得分:5)
正如Matt指出的那样,您可以为具有不同开始时间的同一层创建包含不同动画的动画组。您还可以为独立的CAAnimation
对象或CAAnimation
组设置委托,并且在每个动画完成后,它将调用animationDidStop:finished:
委托方法(请注意,作为组的一部分的动画获胜不要打电话给他们的代理animationDidStop:finished:
方法。
我发现了一个很酷的技巧,它使CAAnimation animationDidStop:finished:
方法更强大。我使用方法setValue:forKey:
将一个代码块添加到一个独立的动画或动画组,使用键@“animationCompletionBlock”。然后我编写了一个通用的animationDidStop:finished:
方法来检查刚刚完成的@“animationCompletionBlock”键的动画,如果找到它,则在那里执行代码块。
在github上查看这个项目,了解该技术的一个实例:
CAAnimation demo with completion blocks
您还可以在
中设置一组动画[CATransaction begin];
//...
[[CATransaction commit];
阻止,如你所说。执行此操作时,可以使用CATransaction
类方法setCompletionBlock:
在当前事务组中的所有动画完成时调用代码块。然后,一个事务的完成块可以触发下一个事务。
答案 2 :(得分:1)
我使用setCompletionBlock
方法将其关闭以定义一个闭包,当第一个动画完成时触发下一个动画:
[CATransaction begin]
layer1.property = new_property;
CATransaction.setCompletionBlock {
[CATransaction begin]
layer2.property2 = new_property2;
[CATransaction commit];
}
[CATransaction commit];
答案 3 :(得分:0)
我不相信你可以像在你的例子中那样“嵌套”CA动画。
您需要为动画指定委托,并将第二个“转换”放在委托的animationDidStop:finished:
选择器中。
可能想看一下Apple的Animation Types & Timing Programming Guide。
答案 4 :(得分: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"];
};
更灵活:)
我上传了完成处理程序here的代码。但是看一下头文件中的通知。我真的很困惑为什么这个方法没有被调用。
答案 5 :(得分:-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.
这显然取决于一些“外部魔法”,但这个概念很简单,并且(通过依赖性)消除了“处理”任何类型的粗略授权的需要。特别是{ {1}}等类别来自伟大的FunSize Framework, on Github。