CAEmitterLayer 与 CACurrentMediaTime() 的计时不正确,有时根本不显示
CAEmitterLayer not timing correctly with CACurrentMediaTime() and sometimes not showing at all
我目前正在使用 CAEmitterLayer 和 运行 制作粒子发射器,以解决当我启动动画时层预加载动画的问题,因此当我显示它时粒子到处都是。
许多答案都说罪魁祸首是 CAEmitterLayer 被预加载,我们只需要在发射器上将其 beginTime 设置为 CACurrentMediaTime() 即可。
参见:
CAEmitterLayer emits random unwanted particles on touch events
iOS 7 CAEmitterLayer spawning particles inappropriately
对我来说这个解决方案没有用,当运行在设备上使用它时,iPad Air 运行ning iOS 12.1,发射器经常不显示并且有时会延迟很长时间。
为了说明这个问题,我在 github 上做了一个项目:
https://github.com/roodoodey/CAEmitterLayer/tree/master/CAEmitterLayerApp
这是主要代码,我为在 运行dom 中选择的粒子提供了 7 个不同的图像,还有一个按钮在按下时显示发射器。
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]
}
}
这是应用 运行ning 在设备上的 20 秒短视频,iPad Air 运行ning iOS 12.1,而不是 运行 通过xcode。 https://www.dropbox.com/s/f9uol3yot67drm8/ScreenRecording_11-25-2018%2013-19-29.MP4?dl=0
如果有人能看到他们是否可以重现此问题或阐明此 st运行ge 行为,我们将不胜感激。
我对 Core Animation 有很多经验,尽管我不得不承认对 CAEmitterLayer 的了解并不多。一切看起来都很好,对于一个非常了解 CALayer 的人来说,使用 CACurrentMediaTime() 设置 beginTime 是有意义的。但是,我 运行 你的项目发现它没有工作。对我来说,在单元格上设置 beginTime 会产生我预期的效果。
含义
//delay for 5.0 seconds
cell.beginTime = CACurrentMediaTime() + 5.0
cell.beginTime = CACurrentMediaTime() //immediate
cell.beginTime = CACurrentMediaTime() - 5.0 //5 seconds ago
整个文件
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]
}
}
我目前正在使用 CAEmitterLayer 和 运行 制作粒子发射器,以解决当我启动动画时层预加载动画的问题,因此当我显示它时粒子到处都是。
许多答案都说罪魁祸首是 CAEmitterLayer 被预加载,我们只需要在发射器上将其 beginTime 设置为 CACurrentMediaTime() 即可。
参见:
CAEmitterLayer emits random unwanted particles on touch events
iOS 7 CAEmitterLayer spawning particles inappropriately
对我来说这个解决方案没有用,当运行在设备上使用它时,iPad Air 运行ning iOS 12.1,发射器经常不显示并且有时会延迟很长时间。
为了说明这个问题,我在 github 上做了一个项目: https://github.com/roodoodey/CAEmitterLayer/tree/master/CAEmitterLayerApp
这是主要代码,我为在 运行dom 中选择的粒子提供了 7 个不同的图像,还有一个按钮在按下时显示发射器。
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]
}
}
这是应用 运行ning 在设备上的 20 秒短视频,iPad Air 运行ning iOS 12.1,而不是 运行 通过xcode。 https://www.dropbox.com/s/f9uol3yot67drm8/ScreenRecording_11-25-2018%2013-19-29.MP4?dl=0
如果有人能看到他们是否可以重现此问题或阐明此 st运行ge 行为,我们将不胜感激。
我对 Core Animation 有很多经验,尽管我不得不承认对 CAEmitterLayer 的了解并不多。一切看起来都很好,对于一个非常了解 CALayer 的人来说,使用 CACurrentMediaTime() 设置 beginTime 是有意义的。但是,我 运行 你的项目发现它没有工作。对我来说,在单元格上设置 beginTime 会产生我预期的效果。
含义
//delay for 5.0 seconds
cell.beginTime = CACurrentMediaTime() + 5.0
cell.beginTime = CACurrentMediaTime() //immediate
cell.beginTime = CACurrentMediaTime() - 5.0 //5 seconds ago
整个文件
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]
}
}