Help debugging CAKeyframeAnimation that isn't behaving as intended

Discussion in 'Mac Programming' started by GorillaPaws, Aug 18, 2011.

  1. GorillaPaws macrumors 6502a

    GorillaPaws

    Joined:
    Oct 26, 2003
    Location:
    Richmond, VA
    #1
    I'm playing around with a test app that draws a hexagon in a CAShapeLayer, and has 3 CAEmitterLayers positioned at the north, south-east and south-west vertices of the hexagon (I hope that makes sense). The CAShapeLayer and the 3 CAEmitterLayers are all set as to the root layer of their view. The intent is when the hexagon is clicked, a keyframe animation is triggered so each CAEmitterLayer traces the outline of the hex, leaving a trail behind. I am doing this by iterating through each CAEmitterLayer and setting it's respective path as a CAKeyframeAnimation and then adding the animation to an array that I pass into a CAAnimationGroup in order to trigger all animations simultaneously. Currently when I select the hex the 3 emission layers light up, but don't move as expected.

    I know the hit-testing is functioning properly (because the "setEnabled" property is functioning as expected), and I've tested the code elsewhere. Also the "cycleValuesForArray:quantityOfArrays:" should be working fine. I've tested it in a separate project and it behaved as expected. I suspect my problem is related to getting my key paths right. I'm trying to create 3 unique key paths, one for each CAEmitterLayer instance. I use the same procedure to assign the "name" property in my setup method that fires in the awakeFromNib. Here is the method in question:

    Code:
    -(void) animateSelectionDust
    {
    	NSArray *tSelectionSparklerArray = [NSArray arrayWithObjects: _selectionSparklerA,
    								      _selectionSparklerB,
    								      _selectionSparklerC, nil];
    	//	create an array that we'll add the animations into and later pass into a CAAnimationGroup
    	NSMutableArray *tAnimationsForAnimationGroup = [NSMutableArray array];
    
    	//	tArrayOfArrays will hold several arrays of points which we'll use to create paths for our respective selectionSparkler emitter objects
    	NSMutableArray *tArrayOfArrays = [NSMutableArray array];
    	tArrayOfArrays = [self cycleValuesForArray: [[self hexTile] perimeterPointsMArray] 
    			          quantityOfArrays: 3];
    	
    	for( NSInteger i = 0; i < [tSelectionSparklerArray count]; i++ )
    	{
    		CAEmitterLayer *tSelectionSparkler = [tSelectionSparklerArray objectAtIndex: i];
    		[tSelectionSparkler setValue: [NSNumber numberWithBool: YES] 
    				  forKeyPath: @"emitterCells.DISelectionDust.enabled"];
    		
    		CGMutablePathRef tSparklerPath = CGPathCreateMutable();
    		tSparklerPath = [self CGPath: tSparklerPath 
    			 FromMArray: [tArrayOfArrays objectAtIndex: i]];
    		NSString *tPositionSuffix = [NSString stringWithFormat: @"_%ld.position", (long)i];
    		NSString *tInstanceKeypath = [kDISelectionEmitter stringByAppendingString: tPositionSuffix];
    		CAKeyframeAnimation *tKeyframeAnimation = [CAKeyframeAnimation animationWithKeyPath: tInstanceKeypath];
    		[tKeyframeAnimation setPath: tSparklerPath];
    		CFRelease( tSparklerPath );
    		[tKeyframeAnimation setCalculationMode: kCAAnimationPaced];
    
    		[tAnimationsForAnimationGroup insertObject: tKeyframeAnimation atIndex: i];
    	}
    	CAAnimationGroup *tAnimationGroup = [CAAnimationGroup animation];
    	[tAnimationGroup setAnimations: tAnimationsForAnimationGroup];
    	[tAnimationGroup setDuration: 6.0];
    	[_rootLayer addAnimation: tAnimationGroup 
    			  forKey: @"position"];
    }
    
    One of the more frustrating challenges here is that when I try to inspect these Core Animation objects in the debugger, the properties are opaque, so I can't tell if properties are being set correctly. I would be happy to post the entire project if that would be helpful (although I'm somewhat embarrassed about it's current state);
     
  2. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #2
    I could take a look at it (even though I haven't used CA)... but one thing that's probably not related, but is problematic to me is:
    Code:
    CGMutablePathRef tSparklerPath = CGPathCreateMutable();
    		tSparklerPath = [self CGPath: tSparklerPath 
    			 FromMArray: [tArrayOfArrays objectAtIndex: i]];
    ...
    CFRelease( tSparklerPath );
    In the first line you assign a pointer (Refs are typedef'd pointers), and immediately afterwards assign a new (maybe?) pointer. Does CGPth:FromMArray return the CGMutablePathRef that's passed in, modifying it with CGPath... functions? If so, no need to re-assign. If not, then you've lost the new struct you allocated in the first line and can never free it.

    -Lee
     
  3. GorillaPaws thread starter macrumors 6502a

    GorillaPaws

    Joined:
    Oct 26, 2003
    Location:
    Richmond, VA
    #3
    Yes, it's the same reference. Originally, the method itself created the reference, but there's no CFAutorelease() function, so I changed the method around, but forgot to remove the assignment there. Thanks for spotting it.

    Here's the link to download the project .zip.
     
  4. GorillaPaws thread starter macrumors 6502a

    GorillaPaws

    Joined:
    Oct 26, 2003
    Location:
    Richmond, VA
    #4
    So the property I'm trying to set is "emitterPosition" not "position".

    I have corrected this, and it's still not behaving as expected. To be honest, I'm not 100% clear on what the keyPath of my CAEmitterLayers ought to be. From the docs: "The key path is relative to the layer the receiver is attached to."

    My CAEmitterLayers are attached to my _rootLayer. So is my key path:

    "emitterLayer.INSTANCE_ID.emitterPosition"
    "emitterLayers.INSTANCE_ID.emitterPosition" (note the s on layers)
    or simply
    "INSTANCE_ID.emitterPositon"

    do I need to include the _rootLayer in the path as well?

    where INSTANCE_ID is a unique "name" property for each of the 3 instances of CAEmitterLayers. I have tried all of these combinations, with no luck so I'm no longer 100% convinced that my issue is a key path.

    This KVC stuff is driving me batty, because it will just silently fail if you give it the wrong key path, and there's no good way to inspect it from what I've seen.
     

Share This Page