【问题标题】:CAEmitterLayer not timing correctly with CACurrentMediaTime() and sometimes not showing at allCAEmitterLayer 没有用 CACurrentMediaTime() 正确计时,有时根本不显示
【发布时间】:2018-11-25 13:43:34
【问题描述】:

我目前正在使用 CAEmitterLayer 制作粒子发射器,但在启动它时遇到了层预加载动画的问题,因此当我显示它时粒子到处都是。

许多答案都说罪魁祸首是 CAEmitterLayer 被预加载,我们只需在发射器上将其 beginTime 设置为 CACurrentMediaTime()。

见:

CAEmitterLayer emits random unwanted particles on touch events

Initial particles from CAEmitterLayer don't start at emitterPosition

iOS 7 CAEmitterLayer spawning particles inappropriately

对我来说,这个解决方案不起作用,当在设备上运行它时,运行 iOS 12.1 的 iPad Air,发射器经常不显示,有时显示延迟很大。

为了说明这个问题,我在 github 上做了一个项目: https://github.com/roodoodey/CAEmitterLayer/tree/master/CAEmitterLayerApp

这里是主要代码,我有 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]
    }


}

这是一段 20 秒的短视频,展示了在设备上运行的应用程序,iPad Air 运行 iOS 12.1,而不是通过 xcode 运行。 https://www.dropbox.com/s/f9uol3yot67drm8/ScreenRecording_11-25-2018%2013-19-29.MP4?dl=0

如果有人可以查看他们是否可以重现此问题或阐明这种奇怪的行为,将不胜感激。

【问题讨论】:

    标签: ios swift caemitterlayer caemittercell


    【解决方案1】:

    我在 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]
        }
    
    
    }
    

    【讨论】:

    • 你是绝对正确的,在单元格上设置 CACurrentMediaTime() 是有效的,我已经在我的示例项目中确认了。这是非常奇怪的行为,并且记录非常差。感谢您的解决方案,我很感激!
    猜你喜欢
    • 2019-09-07
    • 2018-02-05
    • 2020-03-23
    • 1970-01-01
    • 1970-01-01
    • 2021-03-31
    • 1970-01-01
    • 2010-12-09
    相关资源
    最近更新 更多