15

My goal is to make an explosion-like animation where many particles are emitted in a short duration. My problem is that CAEmitterLayer, when it begins emitting, adds "future" particles to make it looks like the animation has been running for a while.

How can I disable or workaround this? When I increase the birthRate, I only want particles to start appearing from the emitterPosition, and not at all points along the CAEmitterCell's projected lifetime. Any help is appreciated.

#import "EmitterView.h"

@interface EmitterView ()

@property CAEmitterLayer* emitter;

@end

@implementation EmitterView

- (void) awakeFromNib {
    [super awakeFromNib];
    self.emitter = (CAEmitterLayer*)self.layer;

    CAEmitterCell* snowflake = [CAEmitterCell emitterCell];
    snowflake.contents = (id)[[UIImage imageNamed: @"snowflake"] CGImage];
    snowflake.lifetime = 3;
    snowflake.birthRate = 1;
    snowflake.velocity = 50;
    snowflake.emissionRange = 3.1415;

    self.emitter.birthRate = 0;
    self.emitter.emitterCells = [NSArray arrayWithObject: snowflake];
    self.emitter.emitterPosition = CGPointMake(100, 100);
    self.emitter.emitterSize = CGSizeMake(0, 0);
    self.emitter.emitterShape = kCAEmitterLayerPoint;
}

+ (Class) layerClass {
    return [CAEmitterLayer class];
}

- (void) burst {
    self.emitter.birthRate = 10;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
        self.emitter.birthRate = 0;
    });
}

@end
WaltersGE1
  • 813
  • 7
  • 26

1 Answers1

36

This behavior actually changed with the release of iOS 7 and apparently hasn't gone back since then. It used to behave the way you would expect back before iOS 7, but either Apple introduced a bug that caused this, or they chose to change the behavior without telling anybody.

I filed a bug for this exact issue on August 28, 2013, which was closed as a duplicate of another bug report for the same issue. Apple's bug reporter is reporting that the other bug is still open, which means Apple hasn't addressed it yet, despite more than a year and a half to take care of it.

I recommend filing your own bug report with Apple, giving them a simple app that demonstrates the behavior, and maybe getting some renewed attention to it will help get them to do something about it.

EDIT:

After researching the issue a little, I found out that the solution is to add this line:

self.emitter.beginTime = CACurrentMediaTime();

It's important to know that CAEmitterLayer is a subclass of CALayer, which conforms to the CAMediaTiming protocol. Why this whole fact isn't better documented is beyond me.

Note that you probably want to call this from your - (void)burst method, but that if you call it again within a short period of time, while previous particles are still around, you might possibly see some odd behavior because of resetting the beginTime.

Gavin
  • 8,204
  • 3
  • 32
  • 42
  • @WaltersGE1 I found out some more info, which should actually help. Apparently Apple is very poor about documenting some very important things... – Gavin May 01 '15 at 20:43
  • I tried it out and it works. The only issue is that, of course, it resets previous "explosion particles". If I need this, I'll just use multiple CAEmitterLayers. – WaltersGE1 May 03 '15 at 23:09
  • In Xamarin: CAAnimation.CurrentMediaTime() – Bjørn Egil Aug 11 '16 at 04:04
  • 2
    With that line, my animation started working like once in 3 launches, very weird... But this line indeed do the job – Bohdan Savych Jul 07 '20 at 11:33
  • 1
    Still relevant 7 years later. Thank you! – João Pereira Nov 11 '22 at 16:15
  • For someone else running the same issue as @BohdanSavych after setting `emitter.beginTime = CACurrentMediaTime()`, set a delay with `emitter.timeOffset` as layers have their time scale, and apparently, a race condition is happening. FYI, using `emitter.beginTime = emitter.convertTime(CACurrentMediaTime(), from: nil) + someOffset` did not make the trick. – nandodelauni Dec 19 '22 at 12:06