11

I'm trying to make a sequence of animations, I've found in CAAnimationGroup the right class to achieve that object. In practice I'm adding on a view different subviews and I'd like to animate their entry with a bounce effect, the fact is that I want to see their animations happening right after the previous has finished. I know that I can set the delegate, but I thought that the CAAnimationGroup was the right choice.
Later I discovered that the group animation can belong only to one layer, but I need it on different layers on screen. Of course on the hosting layer doesn't work. Some suggestions?

- (void) didMoveToSuperview {
    [super didMoveToSuperview];
    float startTime = 0;
    NSMutableArray * animArray = @[].mutableCopy;
    for (int i = 1; i<=_score; i++) {
        NSData *archivedData = [NSKeyedArchiver archivedDataWithRootObject: self.greenLeaf];
        UIImageView * greenLeafImageView = [NSKeyedUnarchiver unarchiveObjectWithData: archivedData];
        greenLeafImageView.image = [UIImage imageNamed:@"greenLeaf"];
        CGPoint leafCenter =  calculatePointCoordinateWithRadiusAndRotation(63, -(M_PI/11 * i) - M_PI_2);
        greenLeafImageView.center = CGPointApplyAffineTransform(leafCenter, CGAffineTransformMakeTranslation(self.bounds.size.width/2, self.bounds.size.height));
        [self addSubview:greenLeafImageView];

        //Animation creation
        CAKeyframeAnimation *bounceAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale"];
        greenLeafImageView.layer.transform = CATransform3DIdentity;
        bounceAnimation.values = @[
                                  [NSNumber numberWithFloat:0.5],
                                  [NSNumber numberWithFloat:1.1],
                                  [NSNumber numberWithFloat:0.8],
                                  [NSNumber numberWithFloat:1.0]
                                  ];

        bounceAnimation.duration = 2;
        bounceAnimation.beginTime = startTime;
        startTime += bounceAnimation.duration;

        [animArray addObject:bounceAnimation];
        //[greenLeafImageView.layer addAnimation:bounceAnimation forKey:nil];


    }
    // Rotation animation
    [UIView animateWithDuration:1 animations:^{
        self.arrow.transform = CGAffineTransformMakeRotation(M_PI/11 * _score);
    }];
    CAAnimationGroup * group = [CAAnimationGroup animation];
    group.animations = animArray;
    group.duration = [[ animArray valueForKeyPath:@"@sum.duration"] floatValue];
    [self.layer addAnimation:group forKey:nil];
}
4

2 回答 2

27

CAAnimationGroup是为了将多个CAAnimation子类堆叠在一起形成一个动画,例如,一个动画可以执行缩放,另一个可以移动它,而第三个可以旋转它,它不是为了管理多个图层,而是为了拥有多个叠加动画。

就是说,我认为解决您的问题的最简单方法是将每个CAAnimation等价beginTime于所有先前的持续时间的总和,以说明:

for i in 0 ..< 20
{
    let view : UIView = // Obtain/create the view...;
    let bounce = CAKeyframeAnimation(keyPath: "transform.scale")

    bounce.duration  = 0.5;
    bounce.beginTime = CACurrentMediaTime() + bounce.duration * CFTimeInterval(i);

    // ...

    view.layer.addAnimation(bounce, forKey:"anim.bounce")
}

请注意,每个人都会得到duration * i,并且CACurrentMediaTime()在使用该beginTime属性时是必需的(它基本上是“现在”的高精度时间戳,用于动画)。整行可以解释为now + duration * i.

必须注意,如果将 a CAAnimations 添加到 a CAAnimationGroup,则其beginTime变为相对于组的开始时间,因此5.0动画上的值将是5.0整个组开始后的秒数。在这种情况下,您不使用 CACurrentMediaTime()

于 2013-07-21T06:29:25.533 回答
4

如果您查看文档,您会注意到CAAnimationGroup继承自CAAnimation,并且CAAnimation只能分配给一个CALayer。它的目的实际上是让创建和管理您希望同时应用于 a 的多个动画变得容易CALayer,而不是管理多个CALayer对象的动画。

CALayer为了处理不同或对象之间不同动画的排序,UIView我使用的一种技术是为每个对象/动画创建一个NSOperation,然后将它们放入一个NSOperationQueue来管理排序。这有点复杂,因为您必须使用动画完成回调来告诉NSOperation它已经完成,但是如果您编写一个好的动画管理子类NSOperation,它会相当方便并允许您创建复杂的排序路径。实现排序目标的低成本方式是简单地beginTime在对象上设置属性CAAnimation(这来自于它对CAMediaTiming协议的采用)以获得所需的时间。

话虽如此,我将向您指出一些我编写并开源的代码,以解决您描述的完全相同的用例。您可以在 github 上找到它(包括相同的代码)。我将添加以下注释:

  • 我的动画管理代码允许您通过识别图像更改、比例更改、位置更改等的顺序和时间来在 plist 中定义动画。在 plist 文件中而不是在代码中调整动画实际上非常方便和简洁(这就是我写这篇文章的原因)。
  • 如果不希望用户与您创建的子视图交互,那么创建作为子层添加到托管视图层的层对象实际上要好得多(开销更少)。
于 2013-07-20T22:14:09.200 回答