ioscaemitterlayercaemittercell

Initial particles from CAEmitterLayer don't start at emitterPosition


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

Solution

  • 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.