Создание вертикального CAEmitterLayer

Я работаю над созданием CAEmitterLayer, который имеет высоту экрана и отталкивается от -X, поэтому CAEmitterCells перемещаются слева (вне экрана) в верхний правый угол.

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

Вот эмиттер:

emitter.emitterPosition = CGPoint(x: -50, y: (view.frame.height / 2))
emitter.emitterShape = kCAEmitterLayerLine
emitter.emitterSize = CGSize(width: 2, height: view.frame.height)

Я упоминал, что высота emitterSize не работает, потому что, если я изменю ширину вышеупомянутого emitterSize, я могу видеть, как ширина изменяется правильно! Неважно, какое значение я выбрал для высоты... это игнорируется.

И CAEmitterCells

cell.birthRate = 10
cell.lifetime = 10
cell.velocity = CGFloat(50)
cell.emissionLongitude = (45 * (.pi/180))

Как я могу установить ширину emitterSize равной 2 точкам в ширину и высоту вида?

2 ответа

kCAEmitterLayerLine всегда горизонтальная линия нулевой толщины. Только emitterSize.width используется.

Вы можете использовать форму линии по вертикали, установив преобразование слоя эмиттера в поворот. Пример детской площадки:

import UIKit
import PlaygroundSupport

class VerticalEmitterView: UIView {

    let emitter = CAEmitterLayer()

    override func layoutSubviews() {
        super.layoutSubviews()

        let bounds = self.bounds
        emitter.position = CGPoint(x: bounds.midX, y: bounds.midY)
        emitter.bounds = CGRect(x: 0, y: 0, width: bounds.size.height, height: bounds.size.width)
        emitter.emitterPosition = CGPoint(x: bounds.width / 2, y: -50)
        emitter.emitterSize = CGSize(width: emitter.bounds.size.width, height: 0)

        if emitter.superlayer != self.layer {
            emitter.setAffineTransform(CGAffineTransform(rotationAngle: -.pi / 2))
            emitter.emitterShape = kCAEmitterLayerLine

            let cell = CAEmitterCell()
            cell.birthRate = 100
            cell.lifetime = 10
            cell.velocity = 50
            cell.emissionLongitude = .pi
            cell.color = UIColor.red.cgColor
            let particleSize = CGSize(width: 5, height: 5)
            let image = UIGraphicsImageRenderer(size: particleSize).image(actions: { (rc) in
                UIColor.white.setFill()
                let gc = UIGraphicsGetCurrentContext()
                UIBezierPath(ovalIn: CGRect(origin: .zero, size: particleSize)).fill()
            })
            cell.contents = image.cgImage
            emitter.emitterCells = [cell]

            layer.addSublayer(emitter)
        }
    }

}

let rootView = UIView(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
rootView.backgroundColor = .white
let emitterView = VerticalEmitterView(frame: rootView.bounds)
rootView.addSubview(emitterView)
PlaygroundPage.current.liveView = rootView

Результат:

демо-версия

(Прерывание связано с тем, что этот GIF представляет собой односекундный цикл.)

У меня была точно такая же проблема. Просматривая документацию Apple, кажется, что если форма эмиттера kCAEmitterLayerLine, она игнорирует высоту эмиттера.

Неидеальный обходной путь, который я нашел, заключался в использовании kcaEmitterLayerRectangle с достаточно большой высотой, чтобы одна сторона прямоугольника была там, где вы хотели, чтобы линия была.

Другие вопросы по тегам