6

I've mostly seen examples of continuous emitters in Swift, and I've found one example in Obj-C by setting the birthRates of the emitter cells to 0.0, but it doesn't seem to work, so I must be doing something wrong. In my example, I can see the message that the birth rate was set to 0 sixteen times, but the particles continue to flow endlessly.

@IBAction func particleBtnAction(_ sender: Any) {

    let emitter = CAEmitterLayer()
    emitter.emitterPosition = CGPoint(x: self.view.frame.size.width / 2, y: -10)
    emitter.emitterShape = kCAEmitterLayerLine
    emitter.emitterSize = CGSize(width: self.view.frame.size.width, height: 2.0)
    emitter.emitterCells = generateEmitterCells()
    self.view.layer.addSublayer(emitter)

    // perform selector after 1.5 seconds when particles start
    perform(#selector(endParticles), with: emitter, afterDelay: 1.5)

}

private func generateEmitterCells() -> [CAEmitterCell] {

    var cells:[CAEmitterCell] = [CAEmitterCell]()
    for index in 0..<16 {
        let cell = CAEmitterCell()
        cell.birthRate = 4.0
        cell.lifetime = 1.0
        cell.lifetimeRange = 0
        cell.velocity = 0.7
        cell.velocityRange = 0
        cell.emissionLongitude = CGFloat(Double.pi)
        cell.emissionRange = 0.5
        cell.spin = 3.5
        cell.spinRange = 0
        cell.scaleRange = 0.25
        cell.scale = 0.1
        cells.append(cell)
    }
    return cells
}

@objc func endParticles(emitterLayer:CAEmitterLayer) {

    for emitterCell in emitterLayer.emitterCells! {
        emitterCell.birthRate = 0.0
        print("birth rate set to 0")
    }

}
Chewie The Chorkie
  • 4,896
  • 9
  • 46
  • 90

4 Answers4

10

Setting the CAEmitterLayer's lifetime to zero stops any new emitterCells being emitted:

@objc func endParticles(emitterLayer:CAEmitterLayer) {
    emitterLayer.lifetime = 0.0
}
ajrlewis
  • 2,968
  • 3
  • 33
  • 67
  • Then how do you restart emitting later? – Coder221 Jun 16 '19 at 17:14
  • 1
    You can set the emitterLayer.lifetime to something other than 0. You'll also potentially want to set `emitterLayer.beginTime = CACurrentMediaTime()` when starting it up again, otherwise sprites may appear where you wouldn't expect them. – Dave Y May 02 '20 at 14:21
0

You can use key paths to assign a name to each cell and loop through them, changing each cell's property when you want to change them:

private func generateEmitterCells() -> [CAEmitterCell] {

    var cells:[CAEmitterCell] = [CAEmitterCell]()
    for index in 0..<16 {
        let cell = CAEmitterCell()
        cell.birthRate = 4.0
        cell.lifetime = 1.0
        cell.lifetimeRange = 0
        cell.velocity = 0.7
        cell.velocityRange = 0
        cell.emissionLongitude = CGFloat(Double.pi)
        cell.emissionRange = 0.5
        cell.spin = 3.5
        cell.spinRange = 0
        cell.scaleRange = 0.25
        cell.scale = 0.1
        cell.name = "cell\(index)" // cell name
        cells.append(cell)
    }
    return cells
}

@objc func endParticles(emitterLayer:CAEmitterLayer) {

    for i in 0..<(emitterLayer.emitterCells?.count ?? 0){

        emitterLayer.setValue(0.0, forKeyPath: "emitterCells.cell\(i).birthRate")
        print("birth rate set to 0")

    }

}
Chewie The Chorkie
  • 4,896
  • 9
  • 46
  • 90
0

You might try setting the isHidden property when you want to endParticles

emitter.isHidden = true

But note that all the cells instantly vanish, no matter when they were emitted, or their lifetime.

Another possibility would be to set all the scale related properties to 0, and then the lifetime and birthrate would not matter, as newly emitted cells would not be visible.

cell.scaleSpeed = 0
cell.scaleRange = 0
cell.scale = 0
user3408691
  • 93
  • 1
  • 1
  • 7
0

What worked for me is this:

let emmitter = CAEmitterLayer()
let cell = makeCell()
emmitter.emitterCells = [cell]
view.layer.addSublayer(emmitter)

 DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
    emmitter.removeFromSuperlayer()
 }