如何在 iOS 应用程序中链接不同的 CAAnimation

Posted

技术标签:

【中文标题】如何在 iOS 应用程序中链接不同的 CAAnimation【英文标题】:How to chain different CAAnimation in an iOS application 【发布时间】:2012-07-31 10:03:11 【问题描述】:

我需要链接动画,CABasicAnimation 或 CAAnimationGroup 但我不知道怎么做,我唯一要做的就是所有动画在同一层同时执行。

我该怎么做?

例如,其内容设置为汽车图像的图层:

第一个:向右移动 X 点

第二个:向左旋转 90ª

第三个:移动X点

4th:缩放图层

所有这些动画都必须以安全的方式执行,但我做不到:S

顺便说一句:我不是英语,如果我在语法上犯了一些错误,请见谅:D

【问题讨论】:

你打算同时做更高级的动画还是只做移动、缩放和旋转(不是 3D)? 是的,我也使用 CATransform3DMakeRotation 等 【参考方案1】:

大卫建议的方法很好,但我会推荐一种不同的方式。

如果你所有的动画都在同一层,你可以创建一个动画组,让每个动画有一个不同的beginTime,其中第一个动画从beginTime0开始,每个新动画在总数之后开始之前动画的持续时间。

但是,如果您的动画位于不同的图层上,则不能使用动画组(动画组中的所有动画都必须作用于同一图层。)在这种情况下,您需要提交单独的动画,每个动画有一个从CACurrentMediaTime() 偏移的beginTime,例如:

CGFloat totalDuration = 0;
CABasicAnimation *animationOne = [CABasicAnimation animationWithKeyPath: @"alpha"];
animationOne.beginTime = CACurrentMediaTime(); //Start instantly.
animationOne.duration = animationOneDuration;
...
//add animation to layer

totalDuration += animationOneDuration;

CABasicAnimation *animationTwo = [CABasicAnimation animationWithKeyPath: @"position"];
animationTwo.beginTime = CACurrentMediaTime() + totalDuration; //Start after animation one.
animationTwo.duration = animationTwoDuration;
...
//add animation to layer

totalDuration += animationTwoDuration;


CABasicAnimation *animationThree = [CABasicAnimation animationWithKeyPath: @"position"];
animationThree.beginTime = CACurrentMediaTime() + totalDuration; //Start after animation three.
animationThree.duration = animationThreeDuration;
...
//add animation to layer

totalDuration += animationThreeDuration;

【讨论】:

如果这对其他人有帮助:如果您确实使用动画组(即这里不是代码 sn-p 而是大卫的第一段),那么 animation.beginTim 是相对于该组... . 所以你不要将它设置为 CACurrentMediaTime(),你只需将它设置为 totalDuration。这让我困惑了几分钟......【参考方案2】:

tl;dr:您需要在之前的完成后手动添加每个动画。


没有添加顺序动画的内置方法。您可以将每个动画的延迟设置为所有先前动画的总和,但我不建议这样做。

相反,我会创建所有动画,并按照它们应该运行的顺序将它们添加到可变数组(使用数组作为队列)。然后通过将自己设置为所有动画的动画代理,您可以在动画完成时获得animationDidStop:finished: 回调。

在该方法中,您将从数组中删除第一个动画(即下一个动画)并将其添加到图层中。由于您是委托人,您将在该动画完成时获得第二个动画,在这种情况下,animationDidStop:finished: 回调将再次运行,并且下一个动画将从可变数组中删除并添加到图层中。

一旦动画数组为空,所有动画都将运行。


一些示例代码可以帮助您入门。首先,您设置所有动画:

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"backgroundColor"];
[animation setToValue:(id)[[UIColor redColor] CGColor]];
[animation setDuration:1.5];
[animation setDelegate:self];
[animation setValue:[view layer] forKey:@"layerToApplyAnimationTo"];

// Configure other animations the same way ...

[self setSequenceOfAnimations:[NSMutableArray arrayWithArray: @[ animation, animation1, animation2, animation3, animation4, animation5 ] ]];

// Start the chain of animations by adding the "next" (the first) animation
[self applyNextAnimation];

然后在委托回调中,您只需再次应用下一个动画

- (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)finished 
    [self applyNextAnimation];


- (void)applyNextAnimation 
     // Finish when there are no more animations to run
    if ([[self sequenceOfAnimations] count] == 0) return;

    // Get the next animation and remove it from the "queue"
    CAPropertyAnimation * nextAnimation = [[self sequenceOfAnimations] objectAtIndex:0];
    [[self sequenceOfAnimations] removeObjectAtIndex:0];

    // Get the layer and apply the animation
    CALayer *layerToAnimate = [nextAnimation valueForKey:@"layerToApplyAnimationTo"];
    [layerToAnimate addAnimation:nextAnimation forKey:nil];

我正在使用自定义键 layerToApplyAnimationTo,以便每个动画都知道它的层(它只适用于 setValue:forKey:valueForKey:)。

【讨论】:

@DuncanC 我觉得它很脆弱。添加、删除或更改动画顺序变得更加繁琐且容易出错。 非常感谢,我认为这两个答案对我来说都是有效的,但大卫的方法对我来说看起来更好。 @RhythmicFistman 您可以以相反的顺序从队列中读取以反向播放序列。如果您还希望单个动画向后播放,则可以将单个动画的 speed 乘以 -1 向后播放它们。 nextAnimation.speed *= -1; @RhythmicFistman 所以也许不是“它失去了能力”,但足够公平。我认为 Duncan 和我的解决方案在非常不同的情况下都有效,这很好。这表明人们有不同的偏好。 FWIW Duncan 的解决方案比我见过的其他一些东西要好得多,我赞成。 @RandyJames 您可以在添加动画时传递一个键,并使用该键查找正确的数组以从中选择下一个动画。 addAnimation:forKey:中的key和animationForKeyPath:中的key路径不一样【参考方案3】:

这是 Swift 中的一个解决方案:

var animations = [CABasicAnimation]()

var animation1 = CABasicAnimation(keyPath: "key_path_1")
// animation set up here, I've included a few properties as an example
animation1.duration = 1.0
animation1.fromValue = 1
animation1.toValue = 0
animations.append(animation1)
    
var animation2 = CABasicAnimation(keyPath: "key_path_2")
animation2.duration = 1.0
animation2.fromValue = 1
animation2.toValue = 0
// setting beginTime is the key to chaining these
animation2.beginTime = 1.0
animations.append(animation2)
    
let group = CAAnimationGroup()
group.duration = 2.0
group.repeatCount = FLT_MAX
group.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
group.animations = animations
    
yourLayer.addAnimation(group, forKey: nil)

【讨论】:

【参考方案4】:

David 的方法或 beginTime 属性都可以用于链接动画。请参阅 http://wangling.me/2011/06/time-warp-in-animation.html - 它阐明了 beginTime 和其他 CAMediaTiming 协议属性的使用。

【讨论】:

【参考方案5】:

使用 KVC。每个动画的 Key 的 setValue。之后在 animationDidStop 中,您可以定义哪些动画已停止并在链中下一个运行。

- (void)moveXrightAnimation 
    CABasicAnimation* theAnimation = ...
    [theAnimation setValue:@"movexright" forKey:@"animationID"];
//animation


- (void)rotate90leftAnimation 
    CABasicAnimation* theAnimation = ...
    [theAnimation setValue:@"rotate90left" forKey:@"animationID"];
//animation


- (void)moveXpointAnimation 
    CABasicAnimation* theAnimation = ...
    [theAnimation setValue:@"movexpoint" forKey:@"animationID"];
//animation


- (void)scaleAnimation 
    CABasicAnimation* theAnimation = ...
    [theAnimation setValue:@"scale" forKey:@"animationID"];
//animation


#pragma mark - CAAnimationDelegate

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag 
    
    if([[anim valueForKey:@"animationID"] isEqual:@"movexright"]) 
        [self rotate90leftAnimation];
    
    if([[anim valueForKey:@"animationID"] isEqual:@"rotate90left"]) 
        [self moveXpointAnimation];
    
    if([[anim valueForKey:@"animationID"] isEqual:@"movexpoint"]) 
        [self scaleAnimation];
    

【讨论】:

以上是关于如何在 iOS 应用程序中链接不同的 CAAnimation的主要内容,如果未能解决你的问题,请参考以下文章

生成到两个不同应用程序的分支 IO 链接?

如何在 AppDelegate 中执行 Segue?

深度链接 Facebook android / ios 问题

如何在 iTunes Connect 中删除 iOS 应用程序版本?

iOS - 如何使用 branch.io 在 Appstore 中测试深度链接

如何在 ios 应用程序中获取翻译数据库