Search code examples
animationcalayerboundscakeyframeanimation

How to animate contentsRect property along a path?


I have a png sprite sheet and corresponding plist file loaded and I'm trying to animate contentsRect property of a CALayer to display a sprite animation from the above mentioned aprite sheet. Here is a code:

CGFloat width = myLayer.frame.size.width;
CGFloat height = myLayer.frame.size.height;
myLayer.bounds = CGRectMake( 0, 0, width, height );
myLayer.contentsGravity = kCAGravityCenter;
myLayer.contents=(id)pImage; // This is my sprite sheet


CAKeyframeAnimation *keyFrameContentsRectAnimation = [CAKeyframeAnimation animationWithKeyPath:@"contentsRect"];
keyFrameContentsRectAnimation.path = pAnimationPath; // This is a path to animate on
keyFrameContentsRectAnimation.values = pRects; // This is an array of normalized CGRects representing sprite sheet
keyFrameContentsRectAnimation.fillMode = kCAFillModeRemoved;
keyFrameContentsRectAnimation.calculationMode = kCAAnimationDiscrete;
keyFrameContentsRectAnimation.duration=pAnimationDuration;
keyFrameContentsRectAnimation.repeatCount = 1;

The above code seems to be working as expected as long as I disable path animation (i.e. comment out path property from keyFrameContentsRectAnimation) - the animation works and contentsRect moves around my sprite sheet.

But here is the problem: all my sprites require some offset within a layer frame in order to get my animation look right(offset varies depending on a transparency crop).

So I figured if I create a path from these offset points that I have and get it into path property of my animation that should resolve the problem. Unfortunately it's not the case... As soon as I add path property, instead of sprites animation I see the whole sprite sheet image for the duration of animation... What did I miss?


Solution

  • Well, after some reading and experimenting I have a working solution... In order to get the sprites show up in the right places I had to add another animation into an animation group and clip CALayer to dimensions of my sprite:

    myLayer.bounds = CGRectMake( 0, 0, 256.0, 256.0 ); //my sprite dimentions
    
    myLayer.contentsGravity = kCAGravityCenter;
    myLayer.contents=(id)pImage; // This is my sprite sheet
    
    
    CAKeyframeAnimation *keyFrameContentsRectAnimation = [CAKeyframeAnimation animationWithKeyPath:@"contentsRect"];
    
    keyFrameContentsRectAnimation.values = pRects; // This is an array of normalized CGRects representing sprite sheet
    keyFrameContentsRectAnimation.fillMode = kCAFillModeRemoved;
    keyFrameContentsRectAnimation.calculationMode = kCAAnimationDiscrete;
    keyFrameContentsRectAnimation.duration=pAnimationDuration;
    keyFrameContentsRectAnimation.repeatCount = 1;  
    
    CAKeyframeAnimation* kfanim = [CAKeyframeAnimation animationWithKeyPath:@"contents"];
    kfanim.path = pAnimationPath;
    kfanim.values = pRects;
    kfanim.fillMode = kCAFillModeBoth;
    kfanim.calculationMode = kCAAnimationDiscrete;
    kfanim.duration=pAnimationDuration;
    kfanim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
    
    
    NSArray *animations = [NSArray arrayWithObjects:keyFrameContentsRectAnimation,
                           kfanim, nil];
    
    CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
    [animationGroup setDuration:pAnimationDuration];
    [animationGroup setRepeatCount: 1];
    [animationGroup setAnimations:animations];