views:

191

answers:

2

The code below handles a stack of CALayers. Every time a new layer is pushed onto the stack the function is locked, and all existing layers and moved down on the screen to make room for the new layer. Once the last layer is done animating the function is unlocked again so that new layers can be pushed.

My problem is that every time this code runs the initial animation on newLayer. In other words, rather than positioning the new layer at CGPointMake(0, 0-offset) and then animating it to CGPointMake(0, currentLayer.position.y + offset) it shows up at its final position instantly. Am I missing something? Thanks!

-(void)addNewLayerWithHeight:(float)layerHeight {
    if(!animationLocked) {
        animationLocked = YES;
        //Offset is the ammount that each existing layer will need to be moved down by
        int offset = layerHeight;

        //Create the new layer
        CALayer *newLayer = [CALayer layer];
        [newLayer setBounds:CGRectMake(0, 0, self.view.layer.bounds.size.width, layerHeight)];
        [newLayer setAnchorPoint:CGPointMake(0, 0)];
        [newLayer setPosition:CGPointMake(0, 0-offset)];
        [newLayer setBackgroundColor:[[UIColor redColor] CGColor]];

        //Add the new layer to the view's layer and to layerArray
        [self.view.layer addSublayer:newLayer];
        [layerArray insertObject:newLayer atIndex:0];

        //loop through all layers and move them to their new position...
        for(int i=0;i<[layerArray count]; i++) {
            CALayer *currentLayer = [layerArray objectAtIndex:i];

            CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"position"];
            [anim setValue:@"stackLayer" forKey:@"kind"];
            [anim setValue:[NSNumber numberWithInt:i] forKey:@"index"];
            [anim setDelegate:self];
            [anim setDuration:1.0];

            currentLayer.actions = [NSDictionary dictionaryWithObject:anim forKey:@"position"];
            currentLayer.position = CGPointMake(0, currentLayer.position.y + offset);
        }
    }
}

-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
    //Make sure the last layer finished animating...
    if([[anim valueForKey:@"kind"] isEqual:@"stackLayer"] && [[anim valueForKey:@"index"] isEqual:[NSNumber numberWithInt:[layerArray count]-1]]) {
        animationLocked = NO;
    }
}
A: 

You have to set new layer position in animation, not directly on layer.

CAlayer *layer = ...

CABasicAnimation *positionAnimation = [CABasicAnimation animationWithKeyPath:@"transform.translation"];]
positionAnimation.fromValue = oldPOsition
positionAnimation.toValue = newPosition
positionAnimation.duration = n;
positionAnimation.delegate = self;          

[layerToAnimate addAnimation:layerAnimation forKey:@"transform.translation"]
TheBlack
The problem is that what you are outlining is an explicit animation, and the layer reverts back to it's origin position when the animation finishes.
carloe
+1  A: 

You're pretty close. I would just change your code in the loop to this:

for(int i=0;i<[layerArray count]; i++)
{
  CALayer *currentLayer = [layerArray objectAtIndex:i];

  CGPoint endPoint = CGPointMake(0, currentLayer.position.y + offset);
  CGPoint currentPoint = [currentLayer position];

  CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"position"];
  [anim setFromValue:[NSValue valueWithCGPoint:currentPoint]];
  [anim setToValue:[NSValue valueWithCGPoint:endPoint]];
  [anim setDelegate:self];
  [anim setDuration:1.0];

  [anim setValue:@"stackLayer" forKey:@"kind"];
  [anim setValue:[NSNumber numberWithInt:i] forKey:@"index"];

  [currentLayer setPosition:endPoint];
  [currentLayer addAnimation:anim forKey:@"position"];
}

This will ensure that your layer animates from the current position to the offset position as well as setting the position for the layer so that it doesn't revert back to its starting position when the animation completes--though you may not get it to work right if you do it all in the same run loop. You might want to set up the layers and add them when your view loads, and then animate them as a response to some other action or by calling -performSelector:withObject:afterDelay passing it some delay that will allow it a chance to get queued for a later run loop iteration.

Matt Long
That works great! Thanks!
carloe