CAEmitterLayer not timing correctly with CACurrentMediaTime() and sometimes not showing at all












0















I am currently making a particle emitter using CAEmitterLayer and ran into the issue of the layer preloading the animation when I start it and hence the particles all over the place when I show it.



Many answers have said the culprit is CAEmitterLayer being preloaded and we simply have to set its beginTime to CACurrentMediaTime() on the emitter.



See:



CAEmitterLayer emits random unwanted particles on touch events



Initial particles from CAEmitterLayer don't start at emitterPosition



iOS 7 CAEmitterLayer spawning particles inappropriately



For me this solution has not worked, when running it on device, iPad Air running iOS 12.1, the emitter often does not show and sometimes it is showed with a great delay.



To illustrate this issue I made a project on github:
https://github.com/roodoodey/CAEmitterLayer/tree/master/CAEmitterLayerApp



Here is the main code, I have 7 different images for particles chosen at random and a button to show the emitter when pressed.



import UIKit

class ViewController: UIViewController {

var particleImages = [UIImage]()
var emitter: CAEmitterLayer?

override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
view.backgroundColor = UIColor.black

// Populate the random images array for the particles
for index in 1..<8 {
if let image = UIImage(named: "StarParticle00(index)") {
particleImages.append(image)
}

}

// Button pressed to make the emitter emit the particles
let button = UIButton(frame: CGRect(x: view.frame.width * 0.5 - 60, y: view.frame.height * 0.5 - 40, width: 120, height: 80))
button.setTitle("Emit!", for: .normal)
button.setTitleColor(UIColor.white, for: .normal)
button.backgroundColor = UIColor.blue
button.addTarget(self, action: #selector(changeButton(sender:)), for: .touchDown)
button.addTarget(self, action: #selector(addEmitter(sender:)), for: .touchUpInside)
view.addSubview(button)

}

@objc func changeButton(sender: UIButton) {
sender.alpha = 0.5
}

@objc func addEmitter(sender: UIButton) {

sender.alpha = 1.0

// IF an emitter already exists remove it.
if emitter?.superlayer != nil {
emitter?.removeFromSuperlayer()
}

emitter = CAEmitterLayer()
emitter?.emitterShape = CAEmitterLayerEmitterShape.point
emitter?.position = CGPoint(x: self.view.frame.width * 0.5, y: self.view.frame.height * 0.5)
// So that the emitter starts now, and is not preloaded.
emitter?.beginTime = CACurrentMediaTime()

var cells = [CAEmitterCell]()
for _ in 0..<40 {
let cell = CAEmitterCell()
cell.birthRate = 1
cell.lifetime = 3
cell.lifetimeRange = 0.5
cell.velocity = 500
cell.velocityRange = 100
cell.emissionRange = 2 * CGFloat(Double.pi)
cell.contents = getRandomImage().cgImage
cell.scale = 1
cell.scaleRange = 0.5
cells.append(cell)
}

emitter?.emitterCells = cells

view.layer.addSublayer( emitter! )

}

func getRandomImage() -> UIImage {

let upperBound = UInt32(particleImages.count)
let randomIndex = Int(arc4random_uniform( upperBound ))

return particleImages[randomIndex]
}


}


Here is a short 20 second video of the app running on device, iPad Air running iOS 12.1, not run through xcode. https://www.dropbox.com/s/f9uol3yot67drm8/ScreenRecording_11-25-2018%2013-19-29.MP4?dl=0



If somebody could see if they can reproduce this issue or shed some light on this strange behavior it would be greatly appreciated.










share|improve this question





























    0















    I am currently making a particle emitter using CAEmitterLayer and ran into the issue of the layer preloading the animation when I start it and hence the particles all over the place when I show it.



    Many answers have said the culprit is CAEmitterLayer being preloaded and we simply have to set its beginTime to CACurrentMediaTime() on the emitter.



    See:



    CAEmitterLayer emits random unwanted particles on touch events



    Initial particles from CAEmitterLayer don't start at emitterPosition



    iOS 7 CAEmitterLayer spawning particles inappropriately



    For me this solution has not worked, when running it on device, iPad Air running iOS 12.1, the emitter often does not show and sometimes it is showed with a great delay.



    To illustrate this issue I made a project on github:
    https://github.com/roodoodey/CAEmitterLayer/tree/master/CAEmitterLayerApp



    Here is the main code, I have 7 different images for particles chosen at random and a button to show the emitter when pressed.



    import UIKit

    class ViewController: UIViewController {

    var particleImages = [UIImage]()
    var emitter: CAEmitterLayer?

    override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    view.backgroundColor = UIColor.black

    // Populate the random images array for the particles
    for index in 1..<8 {
    if let image = UIImage(named: "StarParticle00(index)") {
    particleImages.append(image)
    }

    }

    // Button pressed to make the emitter emit the particles
    let button = UIButton(frame: CGRect(x: view.frame.width * 0.5 - 60, y: view.frame.height * 0.5 - 40, width: 120, height: 80))
    button.setTitle("Emit!", for: .normal)
    button.setTitleColor(UIColor.white, for: .normal)
    button.backgroundColor = UIColor.blue
    button.addTarget(self, action: #selector(changeButton(sender:)), for: .touchDown)
    button.addTarget(self, action: #selector(addEmitter(sender:)), for: .touchUpInside)
    view.addSubview(button)

    }

    @objc func changeButton(sender: UIButton) {
    sender.alpha = 0.5
    }

    @objc func addEmitter(sender: UIButton) {

    sender.alpha = 1.0

    // IF an emitter already exists remove it.
    if emitter?.superlayer != nil {
    emitter?.removeFromSuperlayer()
    }

    emitter = CAEmitterLayer()
    emitter?.emitterShape = CAEmitterLayerEmitterShape.point
    emitter?.position = CGPoint(x: self.view.frame.width * 0.5, y: self.view.frame.height * 0.5)
    // So that the emitter starts now, and is not preloaded.
    emitter?.beginTime = CACurrentMediaTime()

    var cells = [CAEmitterCell]()
    for _ in 0..<40 {
    let cell = CAEmitterCell()
    cell.birthRate = 1
    cell.lifetime = 3
    cell.lifetimeRange = 0.5
    cell.velocity = 500
    cell.velocityRange = 100
    cell.emissionRange = 2 * CGFloat(Double.pi)
    cell.contents = getRandomImage().cgImage
    cell.scale = 1
    cell.scaleRange = 0.5
    cells.append(cell)
    }

    emitter?.emitterCells = cells

    view.layer.addSublayer( emitter! )

    }

    func getRandomImage() -> UIImage {

    let upperBound = UInt32(particleImages.count)
    let randomIndex = Int(arc4random_uniform( upperBound ))

    return particleImages[randomIndex]
    }


    }


    Here is a short 20 second video of the app running on device, iPad Air running iOS 12.1, not run through xcode. https://www.dropbox.com/s/f9uol3yot67drm8/ScreenRecording_11-25-2018%2013-19-29.MP4?dl=0



    If somebody could see if they can reproduce this issue or shed some light on this strange behavior it would be greatly appreciated.










    share|improve this question



























      0












      0








      0


      1






      I am currently making a particle emitter using CAEmitterLayer and ran into the issue of the layer preloading the animation when I start it and hence the particles all over the place when I show it.



      Many answers have said the culprit is CAEmitterLayer being preloaded and we simply have to set its beginTime to CACurrentMediaTime() on the emitter.



      See:



      CAEmitterLayer emits random unwanted particles on touch events



      Initial particles from CAEmitterLayer don't start at emitterPosition



      iOS 7 CAEmitterLayer spawning particles inappropriately



      For me this solution has not worked, when running it on device, iPad Air running iOS 12.1, the emitter often does not show and sometimes it is showed with a great delay.



      To illustrate this issue I made a project on github:
      https://github.com/roodoodey/CAEmitterLayer/tree/master/CAEmitterLayerApp



      Here is the main code, I have 7 different images for particles chosen at random and a button to show the emitter when pressed.



      import UIKit

      class ViewController: UIViewController {

      var particleImages = [UIImage]()
      var emitter: CAEmitterLayer?

      override func viewDidLoad() {
      super.viewDidLoad()
      // Do any additional setup after loading the view, typically from a nib.
      view.backgroundColor = UIColor.black

      // Populate the random images array for the particles
      for index in 1..<8 {
      if let image = UIImage(named: "StarParticle00(index)") {
      particleImages.append(image)
      }

      }

      // Button pressed to make the emitter emit the particles
      let button = UIButton(frame: CGRect(x: view.frame.width * 0.5 - 60, y: view.frame.height * 0.5 - 40, width: 120, height: 80))
      button.setTitle("Emit!", for: .normal)
      button.setTitleColor(UIColor.white, for: .normal)
      button.backgroundColor = UIColor.blue
      button.addTarget(self, action: #selector(changeButton(sender:)), for: .touchDown)
      button.addTarget(self, action: #selector(addEmitter(sender:)), for: .touchUpInside)
      view.addSubview(button)

      }

      @objc func changeButton(sender: UIButton) {
      sender.alpha = 0.5
      }

      @objc func addEmitter(sender: UIButton) {

      sender.alpha = 1.0

      // IF an emitter already exists remove it.
      if emitter?.superlayer != nil {
      emitter?.removeFromSuperlayer()
      }

      emitter = CAEmitterLayer()
      emitter?.emitterShape = CAEmitterLayerEmitterShape.point
      emitter?.position = CGPoint(x: self.view.frame.width * 0.5, y: self.view.frame.height * 0.5)
      // So that the emitter starts now, and is not preloaded.
      emitter?.beginTime = CACurrentMediaTime()

      var cells = [CAEmitterCell]()
      for _ in 0..<40 {
      let cell = CAEmitterCell()
      cell.birthRate = 1
      cell.lifetime = 3
      cell.lifetimeRange = 0.5
      cell.velocity = 500
      cell.velocityRange = 100
      cell.emissionRange = 2 * CGFloat(Double.pi)
      cell.contents = getRandomImage().cgImage
      cell.scale = 1
      cell.scaleRange = 0.5
      cells.append(cell)
      }

      emitter?.emitterCells = cells

      view.layer.addSublayer( emitter! )

      }

      func getRandomImage() -> UIImage {

      let upperBound = UInt32(particleImages.count)
      let randomIndex = Int(arc4random_uniform( upperBound ))

      return particleImages[randomIndex]
      }


      }


      Here is a short 20 second video of the app running on device, iPad Air running iOS 12.1, not run through xcode. https://www.dropbox.com/s/f9uol3yot67drm8/ScreenRecording_11-25-2018%2013-19-29.MP4?dl=0



      If somebody could see if they can reproduce this issue or shed some light on this strange behavior it would be greatly appreciated.










      share|improve this question
















      I am currently making a particle emitter using CAEmitterLayer and ran into the issue of the layer preloading the animation when I start it and hence the particles all over the place when I show it.



      Many answers have said the culprit is CAEmitterLayer being preloaded and we simply have to set its beginTime to CACurrentMediaTime() on the emitter.



      See:



      CAEmitterLayer emits random unwanted particles on touch events



      Initial particles from CAEmitterLayer don't start at emitterPosition



      iOS 7 CAEmitterLayer spawning particles inappropriately



      For me this solution has not worked, when running it on device, iPad Air running iOS 12.1, the emitter often does not show and sometimes it is showed with a great delay.



      To illustrate this issue I made a project on github:
      https://github.com/roodoodey/CAEmitterLayer/tree/master/CAEmitterLayerApp



      Here is the main code, I have 7 different images for particles chosen at random and a button to show the emitter when pressed.



      import UIKit

      class ViewController: UIViewController {

      var particleImages = [UIImage]()
      var emitter: CAEmitterLayer?

      override func viewDidLoad() {
      super.viewDidLoad()
      // Do any additional setup after loading the view, typically from a nib.
      view.backgroundColor = UIColor.black

      // Populate the random images array for the particles
      for index in 1..<8 {
      if let image = UIImage(named: "StarParticle00(index)") {
      particleImages.append(image)
      }

      }

      // Button pressed to make the emitter emit the particles
      let button = UIButton(frame: CGRect(x: view.frame.width * 0.5 - 60, y: view.frame.height * 0.5 - 40, width: 120, height: 80))
      button.setTitle("Emit!", for: .normal)
      button.setTitleColor(UIColor.white, for: .normal)
      button.backgroundColor = UIColor.blue
      button.addTarget(self, action: #selector(changeButton(sender:)), for: .touchDown)
      button.addTarget(self, action: #selector(addEmitter(sender:)), for: .touchUpInside)
      view.addSubview(button)

      }

      @objc func changeButton(sender: UIButton) {
      sender.alpha = 0.5
      }

      @objc func addEmitter(sender: UIButton) {

      sender.alpha = 1.0

      // IF an emitter already exists remove it.
      if emitter?.superlayer != nil {
      emitter?.removeFromSuperlayer()
      }

      emitter = CAEmitterLayer()
      emitter?.emitterShape = CAEmitterLayerEmitterShape.point
      emitter?.position = CGPoint(x: self.view.frame.width * 0.5, y: self.view.frame.height * 0.5)
      // So that the emitter starts now, and is not preloaded.
      emitter?.beginTime = CACurrentMediaTime()

      var cells = [CAEmitterCell]()
      for _ in 0..<40 {
      let cell = CAEmitterCell()
      cell.birthRate = 1
      cell.lifetime = 3
      cell.lifetimeRange = 0.5
      cell.velocity = 500
      cell.velocityRange = 100
      cell.emissionRange = 2 * CGFloat(Double.pi)
      cell.contents = getRandomImage().cgImage
      cell.scale = 1
      cell.scaleRange = 0.5
      cells.append(cell)
      }

      emitter?.emitterCells = cells

      view.layer.addSublayer( emitter! )

      }

      func getRandomImage() -> UIImage {

      let upperBound = UInt32(particleImages.count)
      let randomIndex = Int(arc4random_uniform( upperBound ))

      return particleImages[randomIndex]
      }


      }


      Here is a short 20 second video of the app running on device, iPad Air running iOS 12.1, not run through xcode. https://www.dropbox.com/s/f9uol3yot67drm8/ScreenRecording_11-25-2018%2013-19-29.MP4?dl=0



      If somebody could see if they can reproduce this issue or shed some light on this strange behavior it would be greatly appreciated.







      ios swift caemitterlayer caemittercell






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Nov 25 '18 at 13:49







      Roodey

















      asked Nov 25 '18 at 13:43









      RoodeyRoodey

      156




      156
























          1 Answer
          1






          active

          oldest

          votes


















          1














          I have a ton of experience with Core Animation although I have to admit not a lot with CAEmitterLayer. Everything looks right and for a person that knows CALayer pretty well, setting the beginTime with CACurrentMediaTime() makes sense. However, I ran your project and saw it was not working. For me setting the beginTime on the cell had the effect I would expect.
          Meaning



          //delay for 5.0 seconds
          cell.beginTime = CACurrentMediaTime() + 5.0
          cell.beginTime = CACurrentMediaTime() //immediate
          cell.beginTime = CACurrentMediaTime() - 5.0 //5 seconds ago


          Entire File



          import UIKit

          class ViewController: UIViewController {

          var particleImages = [UIImage]()
          var emitter: CAEmitterLayer?

          override func viewDidLoad() {
          super.viewDidLoad()
          // Do any additional setup after loading the view, typically from a nib.
          view.backgroundColor = UIColor.black

          // Populate the random images array for the particles
          for index in 1..<8 {
          if let image = UIImage(named: "StarParticle00(index)") {
          particleImages.append(image)
          }

          }

          // Button pressed to make the emitter emit the particles
          let button = UIButton(frame: CGRect(x: view.frame.width * 0.5 - 60, y: view.frame.height * 0.5 - 40, width: 120, height: 80))
          button.setTitle("Emit!", for: .normal)
          button.setTitleColor(UIColor.white, for: .normal)
          button.backgroundColor = UIColor.blue
          button.addTarget(self, action: #selector(changeButton(sender:)), for: .touchDown)
          button.addTarget(self, action: #selector(addEmitter(sender:)), for: .touchUpInside)
          view.addSubview(button)

          }

          @objc func changeButton(sender: UIButton) {
          sender.alpha = 0.5
          }

          @objc func addEmitter(sender: UIButton) {

          sender.alpha = 1.0

          // IF an emitter already exists remove it.
          if emitter?.superlayer != nil {
          emitter?.removeFromSuperlayer()
          }

          emitter = CAEmitterLayer()
          emitter?.emitterShape = CAEmitterLayerEmitterShape.point
          emitter?.position = CGPoint(x: self.view.frame.width * 0.5, y: self.view.frame.height * 0.5)
          // So that the emitter starts now, and is not preloaded.
          var cells = [CAEmitterCell]()
          for _ in 0..<40 {
          let cell = CAEmitterCell()
          cell.birthRate = 1
          cell.lifetime = 3
          cell.lifetimeRange = 0.5
          cell.velocity = 500
          cell.velocityRange = 100
          cell.emissionRange = 2 * CGFloat(Double.pi)
          cell.contents = getRandomImage().cgImage
          cell.scale = 1
          cell.scaleRange = 0.5
          cell.beginTime = CACurrentMediaTime()
          cells.append(cell)
          }

          emitter?.emitterCells = cells
          view.layer.addSublayer( emitter! )
          }

          func getRandomImage() -> UIImage {

          let upperBound = UInt32(particleImages.count)
          let randomIndex = Int(arc4random_uniform( upperBound ))

          return particleImages[randomIndex]
          }


          }





          share|improve this answer
























          • You are absolutely right, setting the CACurrentMediaTime() on the cells works, I have confirmed in my sample project. It is very odd behavior though and very poorly documented. Thank you for the solution I appreciate it!

            – Roodey
            Nov 26 '18 at 2:31













          Your Answer






          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "1"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: true,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: 10,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53468082%2fcaemitterlayer-not-timing-correctly-with-cacurrentmediatime-and-sometimes-not%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          1 Answer
          1






          active

          oldest

          votes








          1 Answer
          1






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes









          1














          I have a ton of experience with Core Animation although I have to admit not a lot with CAEmitterLayer. Everything looks right and for a person that knows CALayer pretty well, setting the beginTime with CACurrentMediaTime() makes sense. However, I ran your project and saw it was not working. For me setting the beginTime on the cell had the effect I would expect.
          Meaning



          //delay for 5.0 seconds
          cell.beginTime = CACurrentMediaTime() + 5.0
          cell.beginTime = CACurrentMediaTime() //immediate
          cell.beginTime = CACurrentMediaTime() - 5.0 //5 seconds ago


          Entire File



          import UIKit

          class ViewController: UIViewController {

          var particleImages = [UIImage]()
          var emitter: CAEmitterLayer?

          override func viewDidLoad() {
          super.viewDidLoad()
          // Do any additional setup after loading the view, typically from a nib.
          view.backgroundColor = UIColor.black

          // Populate the random images array for the particles
          for index in 1..<8 {
          if let image = UIImage(named: "StarParticle00(index)") {
          particleImages.append(image)
          }

          }

          // Button pressed to make the emitter emit the particles
          let button = UIButton(frame: CGRect(x: view.frame.width * 0.5 - 60, y: view.frame.height * 0.5 - 40, width: 120, height: 80))
          button.setTitle("Emit!", for: .normal)
          button.setTitleColor(UIColor.white, for: .normal)
          button.backgroundColor = UIColor.blue
          button.addTarget(self, action: #selector(changeButton(sender:)), for: .touchDown)
          button.addTarget(self, action: #selector(addEmitter(sender:)), for: .touchUpInside)
          view.addSubview(button)

          }

          @objc func changeButton(sender: UIButton) {
          sender.alpha = 0.5
          }

          @objc func addEmitter(sender: UIButton) {

          sender.alpha = 1.0

          // IF an emitter already exists remove it.
          if emitter?.superlayer != nil {
          emitter?.removeFromSuperlayer()
          }

          emitter = CAEmitterLayer()
          emitter?.emitterShape = CAEmitterLayerEmitterShape.point
          emitter?.position = CGPoint(x: self.view.frame.width * 0.5, y: self.view.frame.height * 0.5)
          // So that the emitter starts now, and is not preloaded.
          var cells = [CAEmitterCell]()
          for _ in 0..<40 {
          let cell = CAEmitterCell()
          cell.birthRate = 1
          cell.lifetime = 3
          cell.lifetimeRange = 0.5
          cell.velocity = 500
          cell.velocityRange = 100
          cell.emissionRange = 2 * CGFloat(Double.pi)
          cell.contents = getRandomImage().cgImage
          cell.scale = 1
          cell.scaleRange = 0.5
          cell.beginTime = CACurrentMediaTime()
          cells.append(cell)
          }

          emitter?.emitterCells = cells
          view.layer.addSublayer( emitter! )
          }

          func getRandomImage() -> UIImage {

          let upperBound = UInt32(particleImages.count)
          let randomIndex = Int(arc4random_uniform( upperBound ))

          return particleImages[randomIndex]
          }


          }





          share|improve this answer
























          • You are absolutely right, setting the CACurrentMediaTime() on the cells works, I have confirmed in my sample project. It is very odd behavior though and very poorly documented. Thank you for the solution I appreciate it!

            – Roodey
            Nov 26 '18 at 2:31


















          1














          I have a ton of experience with Core Animation although I have to admit not a lot with CAEmitterLayer. Everything looks right and for a person that knows CALayer pretty well, setting the beginTime with CACurrentMediaTime() makes sense. However, I ran your project and saw it was not working. For me setting the beginTime on the cell had the effect I would expect.
          Meaning



          //delay for 5.0 seconds
          cell.beginTime = CACurrentMediaTime() + 5.0
          cell.beginTime = CACurrentMediaTime() //immediate
          cell.beginTime = CACurrentMediaTime() - 5.0 //5 seconds ago


          Entire File



          import UIKit

          class ViewController: UIViewController {

          var particleImages = [UIImage]()
          var emitter: CAEmitterLayer?

          override func viewDidLoad() {
          super.viewDidLoad()
          // Do any additional setup after loading the view, typically from a nib.
          view.backgroundColor = UIColor.black

          // Populate the random images array for the particles
          for index in 1..<8 {
          if let image = UIImage(named: "StarParticle00(index)") {
          particleImages.append(image)
          }

          }

          // Button pressed to make the emitter emit the particles
          let button = UIButton(frame: CGRect(x: view.frame.width * 0.5 - 60, y: view.frame.height * 0.5 - 40, width: 120, height: 80))
          button.setTitle("Emit!", for: .normal)
          button.setTitleColor(UIColor.white, for: .normal)
          button.backgroundColor = UIColor.blue
          button.addTarget(self, action: #selector(changeButton(sender:)), for: .touchDown)
          button.addTarget(self, action: #selector(addEmitter(sender:)), for: .touchUpInside)
          view.addSubview(button)

          }

          @objc func changeButton(sender: UIButton) {
          sender.alpha = 0.5
          }

          @objc func addEmitter(sender: UIButton) {

          sender.alpha = 1.0

          // IF an emitter already exists remove it.
          if emitter?.superlayer != nil {
          emitter?.removeFromSuperlayer()
          }

          emitter = CAEmitterLayer()
          emitter?.emitterShape = CAEmitterLayerEmitterShape.point
          emitter?.position = CGPoint(x: self.view.frame.width * 0.5, y: self.view.frame.height * 0.5)
          // So that the emitter starts now, and is not preloaded.
          var cells = [CAEmitterCell]()
          for _ in 0..<40 {
          let cell = CAEmitterCell()
          cell.birthRate = 1
          cell.lifetime = 3
          cell.lifetimeRange = 0.5
          cell.velocity = 500
          cell.velocityRange = 100
          cell.emissionRange = 2 * CGFloat(Double.pi)
          cell.contents = getRandomImage().cgImage
          cell.scale = 1
          cell.scaleRange = 0.5
          cell.beginTime = CACurrentMediaTime()
          cells.append(cell)
          }

          emitter?.emitterCells = cells
          view.layer.addSublayer( emitter! )
          }

          func getRandomImage() -> UIImage {

          let upperBound = UInt32(particleImages.count)
          let randomIndex = Int(arc4random_uniform( upperBound ))

          return particleImages[randomIndex]
          }


          }





          share|improve this answer
























          • You are absolutely right, setting the CACurrentMediaTime() on the cells works, I have confirmed in my sample project. It is very odd behavior though and very poorly documented. Thank you for the solution I appreciate it!

            – Roodey
            Nov 26 '18 at 2:31
















          1












          1








          1







          I have a ton of experience with Core Animation although I have to admit not a lot with CAEmitterLayer. Everything looks right and for a person that knows CALayer pretty well, setting the beginTime with CACurrentMediaTime() makes sense. However, I ran your project and saw it was not working. For me setting the beginTime on the cell had the effect I would expect.
          Meaning



          //delay for 5.0 seconds
          cell.beginTime = CACurrentMediaTime() + 5.0
          cell.beginTime = CACurrentMediaTime() //immediate
          cell.beginTime = CACurrentMediaTime() - 5.0 //5 seconds ago


          Entire File



          import UIKit

          class ViewController: UIViewController {

          var particleImages = [UIImage]()
          var emitter: CAEmitterLayer?

          override func viewDidLoad() {
          super.viewDidLoad()
          // Do any additional setup after loading the view, typically from a nib.
          view.backgroundColor = UIColor.black

          // Populate the random images array for the particles
          for index in 1..<8 {
          if let image = UIImage(named: "StarParticle00(index)") {
          particleImages.append(image)
          }

          }

          // Button pressed to make the emitter emit the particles
          let button = UIButton(frame: CGRect(x: view.frame.width * 0.5 - 60, y: view.frame.height * 0.5 - 40, width: 120, height: 80))
          button.setTitle("Emit!", for: .normal)
          button.setTitleColor(UIColor.white, for: .normal)
          button.backgroundColor = UIColor.blue
          button.addTarget(self, action: #selector(changeButton(sender:)), for: .touchDown)
          button.addTarget(self, action: #selector(addEmitter(sender:)), for: .touchUpInside)
          view.addSubview(button)

          }

          @objc func changeButton(sender: UIButton) {
          sender.alpha = 0.5
          }

          @objc func addEmitter(sender: UIButton) {

          sender.alpha = 1.0

          // IF an emitter already exists remove it.
          if emitter?.superlayer != nil {
          emitter?.removeFromSuperlayer()
          }

          emitter = CAEmitterLayer()
          emitter?.emitterShape = CAEmitterLayerEmitterShape.point
          emitter?.position = CGPoint(x: self.view.frame.width * 0.5, y: self.view.frame.height * 0.5)
          // So that the emitter starts now, and is not preloaded.
          var cells = [CAEmitterCell]()
          for _ in 0..<40 {
          let cell = CAEmitterCell()
          cell.birthRate = 1
          cell.lifetime = 3
          cell.lifetimeRange = 0.5
          cell.velocity = 500
          cell.velocityRange = 100
          cell.emissionRange = 2 * CGFloat(Double.pi)
          cell.contents = getRandomImage().cgImage
          cell.scale = 1
          cell.scaleRange = 0.5
          cell.beginTime = CACurrentMediaTime()
          cells.append(cell)
          }

          emitter?.emitterCells = cells
          view.layer.addSublayer( emitter! )
          }

          func getRandomImage() -> UIImage {

          let upperBound = UInt32(particleImages.count)
          let randomIndex = Int(arc4random_uniform( upperBound ))

          return particleImages[randomIndex]
          }


          }





          share|improve this answer













          I have a ton of experience with Core Animation although I have to admit not a lot with CAEmitterLayer. Everything looks right and for a person that knows CALayer pretty well, setting the beginTime with CACurrentMediaTime() makes sense. However, I ran your project and saw it was not working. For me setting the beginTime on the cell had the effect I would expect.
          Meaning



          //delay for 5.0 seconds
          cell.beginTime = CACurrentMediaTime() + 5.0
          cell.beginTime = CACurrentMediaTime() //immediate
          cell.beginTime = CACurrentMediaTime() - 5.0 //5 seconds ago


          Entire File



          import UIKit

          class ViewController: UIViewController {

          var particleImages = [UIImage]()
          var emitter: CAEmitterLayer?

          override func viewDidLoad() {
          super.viewDidLoad()
          // Do any additional setup after loading the view, typically from a nib.
          view.backgroundColor = UIColor.black

          // Populate the random images array for the particles
          for index in 1..<8 {
          if let image = UIImage(named: "StarParticle00(index)") {
          particleImages.append(image)
          }

          }

          // Button pressed to make the emitter emit the particles
          let button = UIButton(frame: CGRect(x: view.frame.width * 0.5 - 60, y: view.frame.height * 0.5 - 40, width: 120, height: 80))
          button.setTitle("Emit!", for: .normal)
          button.setTitleColor(UIColor.white, for: .normal)
          button.backgroundColor = UIColor.blue
          button.addTarget(self, action: #selector(changeButton(sender:)), for: .touchDown)
          button.addTarget(self, action: #selector(addEmitter(sender:)), for: .touchUpInside)
          view.addSubview(button)

          }

          @objc func changeButton(sender: UIButton) {
          sender.alpha = 0.5
          }

          @objc func addEmitter(sender: UIButton) {

          sender.alpha = 1.0

          // IF an emitter already exists remove it.
          if emitter?.superlayer != nil {
          emitter?.removeFromSuperlayer()
          }

          emitter = CAEmitterLayer()
          emitter?.emitterShape = CAEmitterLayerEmitterShape.point
          emitter?.position = CGPoint(x: self.view.frame.width * 0.5, y: self.view.frame.height * 0.5)
          // So that the emitter starts now, and is not preloaded.
          var cells = [CAEmitterCell]()
          for _ in 0..<40 {
          let cell = CAEmitterCell()
          cell.birthRate = 1
          cell.lifetime = 3
          cell.lifetimeRange = 0.5
          cell.velocity = 500
          cell.velocityRange = 100
          cell.emissionRange = 2 * CGFloat(Double.pi)
          cell.contents = getRandomImage().cgImage
          cell.scale = 1
          cell.scaleRange = 0.5
          cell.beginTime = CACurrentMediaTime()
          cells.append(cell)
          }

          emitter?.emitterCells = cells
          view.layer.addSublayer( emitter! )
          }

          func getRandomImage() -> UIImage {

          let upperBound = UInt32(particleImages.count)
          let randomIndex = Int(arc4random_uniform( upperBound ))

          return particleImages[randomIndex]
          }


          }






          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Nov 26 '18 at 1:17









          agibson007agibson007

          2,5332714




          2,5332714













          • You are absolutely right, setting the CACurrentMediaTime() on the cells works, I have confirmed in my sample project. It is very odd behavior though and very poorly documented. Thank you for the solution I appreciate it!

            – Roodey
            Nov 26 '18 at 2:31





















          • You are absolutely right, setting the CACurrentMediaTime() on the cells works, I have confirmed in my sample project. It is very odd behavior though and very poorly documented. Thank you for the solution I appreciate it!

            – Roodey
            Nov 26 '18 at 2:31



















          You are absolutely right, setting the CACurrentMediaTime() on the cells works, I have confirmed in my sample project. It is very odd behavior though and very poorly documented. Thank you for the solution I appreciate it!

          – Roodey
          Nov 26 '18 at 2:31







          You are absolutely right, setting the CACurrentMediaTime() on the cells works, I have confirmed in my sample project. It is very odd behavior though and very poorly documented. Thank you for the solution I appreciate it!

          – Roodey
          Nov 26 '18 at 2:31




















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Stack Overflow!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53468082%2fcaemitterlayer-not-timing-correctly-with-cacurrentmediatime-and-sometimes-not%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          A CLEAN and SIMPLE way to add appendices to Table of Contents and bookmarks

          Calculate evaluation metrics using cross_val_predict sklearn

          Insert data from modal to MySQL (multiple modal on website)