views:

106

answers:

2

Hi All,

I have been running into some issues with animating multiple CALayers at the same time, and was hoping someone could point me in the right direction.

My app contains an array of CALayer. The position of each layer is set to (previousLayer.position.y + previousLayer.bounds.height), which basically lays them out similar to a table. I then have a method that, every-time it is called, adds a new layer to the stack and sets its Y position is set to 0. The Y positions of all other layers in the array are then offset by the height of the new layer (essentially pushing all old layers down).

What I am having problems with is preventing the adding of new layers until the previous animation has completed. Is there a way to tell when an implicit animation has finished? Or alternatively, if I use CABasicAnimation and animationDidFinish, is there a way to tell which object finished animating when animationDidFinish is called?

A: 

You can set arbitrary values for keys on your animation object. What this means is that you can associate your layer that you're animating with the animation and then query it in -animationDidStop:finished: You create your animation this way:

CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"position"];
// set other fields...

[anim setValue:layerToAnimate forKey:@"layer"];

// Start the animation
[layerToAnimate addAnimation:anim forKey:nil];

Then check for that value when the animation stops:

- (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)flag
{
    CALayer *animatedLayer = [animation valueForKey:@"layer"];
    // do something with the layer based on some condition...
    // spin off the next animation...

    [CATransaction begin];
    [CATransaction setValue:(id)kCFBooleanTrue
                 forKey:kCATransactionDisableActions];
    [animatedLayer setPosition:position];
    [CATransaction commit];
}

This is explicit animation, but it should give you what you need.

Matt Long
That worked mostly. The code gets called, and I can get the layer object back from the key. But I am experiencing a flicker effect when I try to set layer.position to the position the layer is supposed to be at at the end of the animation. It looks like the animation ends and the layer position is set back to the origin position before my 'layer.position =' gets called. Any idea how I can get around that?
carloe
Yes. If you want to set the position property at the end of the animation, you will have to disable implicit animation in a transaction in your -animationDidStop:finished: method. I'll update my answer with the pertinent code.
Matt Long
The code you posted is identical to what I had, and caused flickering. I just figured out how to get around it and will post the code in a sec. Thanks for all your help!
carloe
A: 

It turns out that rather than adding the CABasicAnimation directly to the CALayer, I had to add it to the layer's 'actions' dictionary... This leaves the layer at it's final position after the animation ends, but still calls the 'animationDidFinish' method.

-(void)startAnimation {
    if(!animating){
        animating = YES;
        for(int i=0; i<[tweets count]; i++) {
            //get the layer
            CETweetLayer *currentLayer = [tweets objectAtIndex:i];

            //setup the orgin and target y coordinates
            float targetY = currentLayer.position.y + offset;

        //setup the animation
        CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"position"];
        anim.delegate = self;
        currentLayer.actions = [NSDictionary dictionaryWithObject:anim forKey:@"position"];
        currentLayer.position = CGPointMake(self.bounds.size.width/2, targetY);
     }
    }
}

And then...

-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
    animating = NO; 
}
carloe