views:

636

answers:

2

Hello folks, 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

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

A: 

You won't be able to animate your shape with CA. You can only animate layer properties in CA, not custom properties.

CA can be fast because it doesn't have to call back to your code to perform animations, if you think about how it is implemented, it basically is just a thread running GL primitives operations with CALayer frames and contents.

CA animations are sets of instructions for CA to run on its background thread, fire and forget (plus callback at animation completion), not callbacks to your code.

Animating the contents of a layer would require CA to call you back to draw at each frame, or to let you instruct CA about how to draw. Neither of those happen.

duncanwilcox
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?
nutsmuggler
setNeedsDisplay is one way, openGL is another. I don't believe you can with CA.
duncanwilcox
You actually can animate shapes using the CAShapeLayer class and animating the 'path' property.
Joe Ricioppo
Joe actually has a nice writeup (with video) about CAShapeLayer: http://tumbljack.com/post/179975074/complex-interpolation-with-cashapelayer-free
Brad Larson
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.
duncanwilcox
+1  A: 

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.

Brad Larson
CAShapeLayer is very buggy in iPhone OS 3.x
dontWatchMyProfile
@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.
Brad Larson