Search code examples
iosobjective-ccore-animationcalayer

Completion block for CAShapeLayer animations


I have an NSArray that contains 8 CAShapeLayer objects. My goal is to animate the fill color from one to another over a certain duration and then to call the same method again. It seems to almost work but the duration is not being taken in to consideration. Here's the code, perhaps you can help?

[CATransaction begin];

for (int i = 0; i < MAX_NUM_LIGHTS; i++)
{
    NSUInteger c = [colorsArray count];

    CAShapeLayer *l = (CAShapeLayer*)llayers[i];

    // the fill color after the anim is complete
    [l setFillColor:((UIColor*)colorsArray[(i+index)%c]).CGColor];

    CABasicAnimation *fillColorAnimation = [CABasicAnimation animationWithKeyPath:@"fillColor"];
    fillColorAnimation.duration = 2.0f;
    fillColorAnimation.toValue = (id)((UIColor*)colorsArray[(i+index)%c]).CGColor;
    fillColorAnimation.repeatCount = 0;
    fillColorAnimation.autoreverses = FALSE;

    [l addAnimation:fillColorAnimation forKey:@"fillColor"];

}

[CATransaction setCompletionBlock:^
{
    if (currEffect != LpyEffect_MultiBall) return;

    NSUInteger c = [colorsArray count];
    int r = (index+1)%c;
    [self simMultiBallEffect:r]; // recursive call, new index
}];

[CATransaction commit];

Solution

  • The documentation of +[CATransaction setCompletionBlock:] says:

    The completion block object that is guaranteed to be called (on the main thread) as soon as all animations subsequently added by this transaction group have completed (or have been removed.) If no animations are added before the current transaction group is committed (or the completion block is set to a different value,) the block will be invoked immediately.

    You're setting the completion block after you've add all the animations. You need to set it before you've added any animations.