user1264176
user1264176

Reputation: 1128

CAEmitterLayer doesn't add basic animation if beginTime was set for it

I am trying to animate an explosion with CAEmitterLayer and a couple of CAEmitterCell. This should happen after a short delay after user sees a view. I start my animation in viewDidAppear.

Particle animation itself works fine, but as in this question Initial particles from CAEmitterLayer don't start at emitterPosition unless I set emitterLayer.beginTime = CACurrentMediaTime() animation appears to user as one that has been running for some time.

Now to actually achieve an explosion I have to stop emitting particles at some point. I try to use this code to setup CABasicAnimation which would stop emitter after some time:

// emitter layer is reused (remove all animations, remove all cells, remove from superlayer)
... // emitter layer setup in a function "explode" which is called from viewDidAppear
emitterLayer.beginTime = CACurrentMediaTime()

let birthRateAnimation = CABasicAnimation(keyPath: "birthRate")
birthRateAnimation.toValue = 0
birthRateAnimation.timingFunction = CAMediaTimingFunction.init(name:kCAMediaTimingFunctionEaseOut)
birthRateAnimation.beginTime = CACurrentMediaTime() + 1
birthRateAnimation.duration = 5
birthRateAnimation.delegate = self // (self is view controller)
birthRateAnimation.setValue("expl", forKey: "animName")

emitterLayer.add(birthRateAnimation, forKey: "birthRateAnimation")
self.view.layer.addSublayer(emitterLayer)

So now with this code birthRateAnimation is not actually triggered. I have logs in animationDidStop and animationDidStart which don't print anything.

Now, if I call explode function on button tap I see no particle animation at all, but in logs I see "animation start" / "animation stop" messages.

Any idea why?

Upvotes: 2

Views: 1339

Answers (2)

Sebastian
Sebastian

Reputation: 8164

Late to the party, but I ran into the very same thing and the solution is to set beginTime of your emitter to CACurrentMediaTime(), e.g.:

let emitter = makeAwesomeEmitter()
emitter.beginTime = CACurrentMediaTime()

let emitterAnimation = makeAwesomeEmitterBirthRateAnimation()
emitterAnimation.beginTime = 1.0 // 1 sec delay
emitter.add(emitterAnimation, forKey: nil)

myView.layer.addSublayer(emitter)

Rationale is that the emitter pre-generates particles to imitate it running already for a while. See also this SO question which brought me to the solution.

Upvotes: 1

ManicJason
ManicJason

Reputation: 546

I found two problems that kept the animation from working correctly.

  1. The animation is on birthRate, but CAEmitterLayer does not have a birthRate property; CAEmitterCell does, though. Give your CAEmitterCell a name and change CABasicAnimation(keyPath: "birthRate") to CABasicAnimation(keyPath: "emitterCells.cellName.birthRate")
  2. Setting the beginTime to CACurrentMediaTime() + 1.0 does not do what you want, because the emitter layer has its own time scale. Changing that to emitterLayer.convertTime(CACurrentMediaTime(), from: nil) + 1.0 does what you want.

Upvotes: 3

Related Questions