Reputation: 3399
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
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