CAEmitterLayer неправильно синхронизируется с CACurrentMediaTime () и иногда вообще не отображается - PullRequest
0 голосов
/ 25 ноября 2018

В настоящее время я создаю излучатель частиц с помощью CAEmitterLayer и столкнулся с проблемой предварительной загрузки слоя при запуске анимации и, следовательно, частиц повсюду, когда я ее показываю.

Многие ответы сказаливиновником является предварительная загрузка CAEmitterLayer, и мы просто должны установить его beginTime в CACurrentMediaTime () на эмиттере.

См .:

CAEmitterLayer испускает случайные нежелательные частицы при сенсорных событиях

Исходные частицы из CAEmitterLayer не запускаются в emitterPosition

iOS 7 CAEmitterLayer порождает частицы неуместно

Для меня это решение не сработало, при запуске его на устройстве iPad Air под управлением iOS 12.1 часто не отображается излучательи иногда это показывается с большой задержкой.

Чтобы проиллюстрировать эту проблему, я сделал проект на 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

Если бы кто-то мог посмотреть, смогут ли они воспроизвести эту проблему или пролить свет на это странное поведение, это было бы очень признательно.

1 Ответ

0 голосов
/ 26 ноября 2018

У меня огромный опыт работы с Core Animation, хотя я должен признать, что с CAEmitterLayer не так много.Все выглядит правильно, и для человека, который хорошо знает CALayer, установка beginTime с помощью CACurrentMediaTime () имеет смысл.Однако я запустил ваш проект и увидел, что он не работает.Для меня установка 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]
    }


}
...