CAEmitterLayer not timing correctly with CACurrentMediaTime() and sometimes not showing at all
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
add a comment |
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
add a comment |
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
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
ios swift caemitterlayer caemittercell
edited Nov 25 '18 at 13:49
Roodey
asked Nov 25 '18 at 13:43
RoodeyRoodey
156
156
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
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]
}
}
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
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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]
}
}
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
add a comment |
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]
}
}
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
add a comment |
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]
}
}
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]
}
}
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
add a comment |
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
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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