Animating a shape with CoreAnimation

15,419

If you are writing this for iPhone OS 3.x (or Snow Leopard), the new CAShapeLayer class should let you do this kind of animation pretty easily. If you have a path that maintains the same number of control points (like in your case), you can set that path to the CAShapeLayer, then animate the path property from that starting value to your final path. Core Animation will perform the appropriate interpolation of the control points in the path to animate it between those two states (more, if you use a CAKeyframeAnimation).

Note that this is a layer, so you will need to add it as a sublayer of your UIView. Also, I don't believe that the path property implicitly animates, so you may need to manually create a CABasicAnimation to animate the change in shape from path 1 to path 2.

EDIT (11/21/2009): Joe Ricioppo has a nice writeup about CAShapeLayer here, including some videos that show off these kinds of animations.

Share:
15,419
nutsmuggler
Author by

nutsmuggler

Humanist with a twist + Irish fiddle player. iOs Designer + developer. Ruby on railler yet often recalcitrant php coder. Original Developer of the Wordpress Events Manager plugin.

Updated on June 08, 2022

Comments

  • nutsmuggler
    nutsmuggler almost 2 years

    I approaching core animation and drawing empirically. I am trying to animate a simple shape; the shape in question is formed by 3 lines plus a bezier curve. A red line is also drawn, to show the curve control points.

    alt text http://img.skitch.com/20091119-1ufar435jdq7nwh8pid5cb6kmm.jpg

    My main controller simply adds this subview and calls the adjustWave method whenever touchesEnd. Here is the code for my shape drawing class. As you see the class has one property, cp1x (the x of the bezier control point 1). This is the value I would like to animate. Mind, this is a dumb attempt ...

    - (void)drawRect:(CGRect)rect {
        float cp1y = 230.0f;
        float cp2x = 100.0f;
        float cp2y = 120.0f;
    
        CGContextRef ctx = UIGraphicsGetCurrentContext(); 
        CGContextClearRect(ctx, rect);
    
        CGMutablePathRef path = CGPathCreateMutable(); 
        CGPathMoveToPoint(path, NULL, 10.0f, 200.0f); 
        CGPathAddCurveToPoint (path, NULL, cp1x, cp1y, cp2x, cp2y, 300.0f, 200.0f);
        CGPathAddLineToPoint(path, NULL, 300.0f, 300.0f); 
        CGPathAddLineToPoint(path, NULL, 10.0f, 300.0f); 
        CGPathCloseSubpath(path); 
        CGContextSetFillColorWithColor(ctx, [UIColor blueColor].CGColor); 
        CGContextAddPath(ctx, path); 
        CGContextFillPath(ctx);
    
        // Drawing a line from control points 1 and 2
        CGContextBeginPath(ctx);
        CGContextSetRGBStrokeColor(ctx,1,0,0,1);
        CGMutablePathRef cp1 = CGPathCreateMutable(); 
        CGPathMoveToPoint(cp1, NULL, cp1x, cp1y); 
        CGPathAddLineToPoint(cp1, NULL, cp2x, cp2y); 
        CGPathCloseSubpath(cp1); 
        CGContextAddPath(ctx, cp1); 
        CGContextStrokePath(ctx);
    }
    
    - (void)adjustWave {
        [UIView beginAnimations:@"movement" context:nil]; 
        [UIView setAnimationDelegate:self]; 
        [UIView setAnimationWillStartSelector:@selector(didStart:context:)]; 
        [UIView setAnimationDidStopSelector:@selector(didStop:finished:context:)]; 
        [UIView setAnimationCurve:UIViewAnimationCurveEaseIn];
        [UIView setAnimationDuration:3.0f]; 
        [UIView setAnimationRepeatCount:3]; 
        [UIView setAnimationRepeatAutoreverses:YES]; 
        cp1x = cp1x + 20.0f;
        [UIView commitAnimations];
    }
    

    The shape doesn't change. If, conversely, I take out the CA code and add a simple `[self setNeedsDisplay] in the last method the shape changes, but obviously without CA. Can you help sme? I am sure I am making a very basic mistake here… Thanks in advance, Davide

    • Joe Ricioppo
      Joe Ricioppo over 14 years
      As Brad says, you can use the CAShapeLayer class to animate an arbitrary shape by building a CABasicAnimation or CAKeyframeAnimation and animating by the 'path' property. I wrote a post about this class if you want to check out the sample code. bit.ly/shapelayer
  • nutsmuggler
    nutsmuggler over 14 years
    I guess I have a lot to learn here :) At any rate, I don't really care about the code I've shown above, it's just a proof of concept. Is there an alternative way to do something similar with CA? Or should I just leave CA alone and call setNeedDisplay?
  • duncanwilcox
    duncanwilcox over 14 years
    setNeedsDisplay is one way, openGL is another. I don't believe you can with CA.
  • Joe Ricioppo
    Joe Ricioppo over 14 years
    You actually can animate shapes using the CAShapeLayer class and animating the 'path' property.
  • Brad Larson
    Brad Larson over 14 years
    Joe actually has a nice writeup (with video) about CAShapeLayer: tumbljack.com/post/179975074/…
  • duncanwilcox
    duncanwilcox over 14 years
    Animating a path would probably solve the specific image above, though nutsmuggler said "the code shown above [is] just a proof of concept". Animating a path is by no means general purpose drawing animation. But I did fail to mention CAShapeLayer, sorry for that.
  • dontWatchMyProfile
    dontWatchMyProfile almost 14 years
    CAShapeLayer is very buggy in iPhone OS 3.x
  • Brad Larson
    Brad Larson almost 14 years
    @mystify - In what way? I've had it work reasonably well for me, but you do need to remember to have the same number of starting and ending control points in your paths or the results are undefined.