2

I am trying to animate a CAEmitterLayer's emitterPosition like this:

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"emitterPosition.x"] ;
animation.toValue = (id) toValue ;
animation.removedOnCompletion = NO ;
animation.duration = self.translationDuration ;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut] ;
animation.completion = ^(BOOL finished)
    {
    [self animateToOtherSide] ;
    } ;
[_emitterLayer addAnimation:animation forKey:@"emitterPosition"] ;
CGPoint newEmitterPosition = CGPointMake(toValue.floatValue, self.bounds.size.height/2.0) ;
_emitterLayer.emitterPosition = newEmitterPosition ;

Note that animation.completion is declared in a category that just calls the corresponding CAAnimation delegate method.

The problem is that this doesn't animate at all and shows the emitter in its final position. I was under the impression that once you add the animation to the layer, you should change the actual model behind it to its final position so that when the animation completes the model is in its final state; i.e., to prevent the animation from "snapping back" to its original position.

I have tried placing the last two lines in the animation.completion block, and this does indeed animate as expected. However, when the animation finishes some particles are intermittently emitted at the emitter's original position. If you put the system under load (for example, scrolling a tableview while the animation is playing), this happens more often.

Another solution I was thinking about is to not move the emitterPosition at all but just move the CAEmitterLayer itself, although I haven't tried that yet.

Thanks in advance for any help you can provide.

leftspin
  • 2,468
  • 1
  • 25
  • 40
  • There's a *very* good WWDC 2012 video on figuring out what's bogging down your animation "under load". – matt Nov 30 '12 at 21:47

1 Answers1

6

Perhaps emitterPosition.x is not a valid key path for animation. Try using emitterPosition instead (and so you'll have to provide CGPoint values wrapped up in an NSValue).

I just tried this on my own machine and it works fine:

CABasicAnimation* ba = [CABasicAnimation animationWithKeyPath:@"emitterPosition"];
ba.fromValue = [NSValue valueWithCGPoint:CGPointMake(30,100)];
ba.toValue = [NSValue valueWithCGPoint:CGPointMake(200,100)];
ba.duration = 6;
ba.autoreverses = YES;
ba.repeatCount = HUGE_VALF;
[emit addAnimation:ba forKey:nil];

Other things to think about:

  • You can typically use nil as the key in addAnimation:forKey:, unless you're going to need to find this animation later (e.g. to remove it or override it in some way). The key path is the important thing.

  • Setting removedOnCompletion to NO is almost always wrong and is typically the last refuge of a scoundrel (i.e. due to not understanding how animation works).

  • If, as you say, setting _emitterLayer.emitterPosition = newEmitterPosition inside the completion block does animate, then when why are you using CABasicAnimation at all? Why not just call UIView animate... and set the emitterPosition in the animations block? If that works, it will kill two birds with one stone, moving the position and animating it too.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • I tried animating emitterPosition without the .x but get the same results. I will try the UIView animation next. Thanks! – leftspin Dec 01 '12 at 17:58
  • But then you have to explain why my code works and yours doesn't. You can find my code in a working downloadable project here (option 4): https://github.com/mattneub/Programming-iOS-Book-Examples/blob/master/ch17p457emitThis/EmitThis/AppDelegate.m So just make your code more like my code. :) – matt Dec 01 '12 at 18:00
  • I think the key is that you're autoreversing the animation. My implementation animates one way then creates a new animation to animate in the reverse direction, so I'm moving the emitter back and forth between the two sides after each animation completes. You're never having to move the emitter position manually because the animation always animates back to its starting position. I'm still messing with this, but when it works I'll be sure to accept your answer. – leftspin Dec 03 '12 at 21:46
  • Are you finding your emitterPosition animation working on an actual iOS 7 device? I am seeing it work in the simulator for both iOS 6.1 and iOS 7, but not on my iPad 2 running iOS 7. Still investigating, though. – tobinjim Sep 30 '13 at 17:59