iosxcodeswiftuicaemitterlayer

How can I make animation with CAEmitterLayer on SwiftUI?


How can I convert this code to SwiftUI. It's a snow effect. I used CAEmitterLayer but I don't know how to use it in SwfitUI. There is no addSublayer in SwiftUI. Is it possible to run this code without using UIHostingController ?

let size = CGSize(width: 824.0, height: 1112.0)
let host = UIView(frame: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height))
self.view.addSubview(host)

let particlesLayer = CAEmitterLayer()
particlesLayer.frame = CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height)

host.layer.addSublayer(particlesLayer)
host.layer.masksToBounds = true

particlesLayer.backgroundColor = UIColor(red: 0.0/255.0, green: 0.0/255.0, blue: 0.0/255.0, alpha: 1.0).cgColor
particlesLayer.emitterShape = .circle
particlesLayer.emitterPosition = CGPoint(x: 509.4, y: 707.7)
particlesLayer.emitterSize = CGSize(width: 1648.0, height: 1112.0)
particlesLayer.emitterMode = .surface
particlesLayer.renderMode = .oldestLast



                let image1 = UIImage(named: "logo")?.cgImage

                let cell1 = CAEmitterCell()
                cell1.contents = image1
                cell1.name = "Snow"
                cell1.birthRate = 92.0
                cell1.lifetime = 20.0
                cell1.velocity = 59.0
                cell1.velocityRange = -15.0
                cell1.xAcceleration = 5.0
                cell1.yAcceleration = 40.0
                cell1.emissionRange = 180.0 * (.pi / 180.0)
                cell1.spin = -28.6 * (.pi / 180.0)
                cell1.spinRange = 57.2 * (.pi / 180.0)
                cell1.scale = 0.06
                cell1.scaleRange = 0.3
                cell1.color = UIColor(red: 255.0/255.0, green: 255.0/255.0, blue: 255.0/255.0, alpha: 1.0).cgColor

                particlesLayer.emitterCells = [cell1]

Solution

  • Here is a solution. Tested with Xcode 11.4 / iOS 13.4

    demo

    struct EmitterView: UIViewRepresentable {
        func makeUIView(context: Context) -> UIView {
            let size = CGSize(width: 824.0, height: 1112.0)
            let host = UIView(frame: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height))
    
            let particlesLayer = CAEmitterLayer()
            particlesLayer.frame = CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height)
    
            host.layer.addSublayer(particlesLayer)
            host.layer.masksToBounds = true
    
            particlesLayer.backgroundColor = UIColor(red: 0.0/255.0, green: 0.0/255.0, blue: 0.0/255.0, alpha: 1.0).cgColor
            particlesLayer.emitterShape = .circle
            particlesLayer.emitterPosition = CGPoint(x: 509.4, y: 707.7)
            particlesLayer.emitterSize = CGSize(width: 1648.0, height: 1112.0)
            particlesLayer.emitterMode = .surface
            particlesLayer.renderMode = .oldestLast
    
    
    
            let image1 = UIImage(named: "logo")?.cgImage
    
            let cell1 = CAEmitterCell()
            cell1.contents = image1
            cell1.name = "Snow"
            cell1.birthRate = 92.0
            cell1.lifetime = 20.0
            cell1.velocity = 59.0
            cell1.velocityRange = -15.0
            cell1.xAcceleration = 5.0
            cell1.yAcceleration = 40.0
            cell1.emissionRange = 180.0 * (.pi / 180.0)
            cell1.spin = -28.6 * (.pi / 180.0)
            cell1.spinRange = 57.2 * (.pi / 180.0)
            cell1.scale = 0.06
            cell1.scaleRange = 0.3
            cell1.color = UIColor(red: 255.0/255.0, green: 255.0/255.0, blue: 255.0/255.0, alpha: 1.0).cgColor
    
            particlesLayer.emitterCells = [cell1]
            return host
        }
    
        func updateUIView(_ uiView: UIView, context: Context) {
        }
    
        typealias UIViewType = UIView
    }
    
    struct TestEmitterLayer: View {
        var body: some View {
            EmitterView()        // << usage !!
        }
    }