Rakesh
Rakesh

Reputation: 3399

CABasicAnimation not working

I am trying to animate a circular stroke (from another SO answer). I am trying to do it in a Cocoa based application.

However it's not working and animationDidStop:finished: is being immediately called with finished flag as NO. Why is this happening? Any pointers on how I can get some information why the finished flag is NO?

Here is the code I use:

Note: quartzPath and NSColorToCGColor are from categories on NSColor and NSBezierPath.

- (IBAction)animateCircle:(id)sender {

    int radius = 10;
    circle = [CAShapeLayer layer];
    // Make a circular shape
    circle.path =  [[NSBezierPath bezierPathWithOvalInRect:NSMakeRect(10, 10, 2.0*radius, 2.0*radius)] quartzPath] ;
    // Center the shape in self.view
    circle.position = CGPointMake(CGRectGetMidX(self.vcForCellView.view.frame)-radius, 
                                  CGRectGetMidY(self.vcForCellView.view.frame)-radius);

    // Configure the apperence of the circle
    circle.fillColor =  [NSColor NSColorToCGColor:[NSColor clearColor]];
    circle.strokeColor = [NSColor NSColorToCGColor:[NSColor blackColor]];
    circle.lineWidth = 5;

    // Add to parent layer
    [self.vcForCellView.view.layer addSublayer:circle];

    // Configure animation
    CABasicAnimation *drawAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
    drawAnimation.duration            = 2.0; // "animate over 10 seconds or so.."
    drawAnimation.repeatCount         = 1.0;  // Animate only once..
    drawAnimation.removedOnCompletion = NO;   // Remain stroked after the animation..

    // Animate from no part of the stroke being drawn to the entire stroke being drawn
    drawAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
    drawAnimation.toValue   = [NSNumber numberWithFloat:1.0f];

    // Experiment with timing to get the appearence to look the way you want
    drawAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];

    drawAnimation.delegate = self;

    // Add the animation to the circle
    [circle addAnimation:drawAnimation forKey:@"drawCircleAnimation"];

}

Also the following code:

-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
    CALayer *layer = [anim valueForKey:@"parentLayer"];
    NSLog(@"%@",layer);
    NSLog(@"%@",flag?@"YES":@"NO");
}

gives the following output:

2013-07-02 19:28:14.760 TicTacToe[16338:707] (null)
2013-07-02 19:28:14.761 TicTacToe[16338:707] NO

UPDATE:

The animationDidStop:... method gets called with finished as YES after I backed the view with a layer ( [view setWantsLayer:YES]). But still nothing is being shown on screen.

Here is the code for getting CGPathRef from NSBezierPath:

- (CGPathRef)quartzPath
{
    int i;
    NSInteger numElements;

    // Need to begin a path here.
    CGPathRef           immutablePath = NULL;

    // Then draw the path elements.
    numElements = [self elementCount];
    if (numElements > 0)
    {
        CGMutablePathRef    path = CGPathCreateMutable();
        NSPoint             points[3];
        BOOL                didClosePath = YES;

        for (i = 0; i < numElements; i++)
        {
            switch ([self elementAtIndex:i associatedPoints:points])
            {
                case NSMoveToBezierPathElement:
                    CGPathMoveToPoint(path, NULL, points[0].x, points[0].y);
                    break;

                case NSLineToBezierPathElement:
                    CGPathAddLineToPoint(path, NULL, points[0].x, points[0].y);
                    didClosePath = NO;
                    break;

                case NSCurveToBezierPathElement:
                    CGPathAddCurveToPoint(path, NULL, points[0].x, points[0].y,
                                          points[1].x, points[1].y,
                                          points[2].x, points[2].y);
                    didClosePath = NO;
                    break;

                case NSClosePathBezierPathElement:
                    CGPathCloseSubpath(path);
                    didClosePath = YES;
                    break;
            }
        }

        // Be sure the path is closed or Quartz may not do valid hit detection.
        if (!didClosePath)
            CGPathCloseSubpath(path);

        immutablePath = CGPathCreateCopy(path);
        CGPathRelease(path);
    }

    return immutablePath;
}

And I'm using 10.7 SDK and can't use the inbuilt CGPath method added with the new SDKs of NSBezierPath.

Upvotes: 1

Views: 1588

Answers (1)

David R&#246;nnqvist
David R&#246;nnqvist

Reputation: 56625

By default views on OS X don't have layers attached to them so the likely problem is that vcForCellView.view.layer is nil. This means that the shape layer never gets added to the layer hierarchy so when the animation is added to the shape layer it is immediately cancelled (as seen by finished being NO).

You can tell your view that it should be backed by a layer using: [myView setWantsLayer:YES];

Upvotes: 4

Related Questions