CAEmitterLayer not emitting particles from view center

56 Views Asked by At

I have the following BoardView which is wrapped in a UIViewRepresentable and shown in a SwiftUI VStack. It is basically a 3x3 grid of UIView tiles. What I'm trying to achieve is that when showWinningAnimation is called on the view, each tile should somewhat explode at very short random time intervals into confetti like particles from the center of the tile projecting particles outwards in all directions and the tile should disappear. The explosion should be similar to a firecracker is how I can best put it. I have the following code but it seems like the explosion is happening at different positions rather than the from the center point of each tile and the projections are also not like that of fireworks. Also is there a better way to make the tile disappear on explosion? At present I'm setting the alpha to 0.0 but I'm not quite happy with the result.

class BoardView: UIView {
    private let tilesPerRow = 3
    private let tileSize = CGSize(width: 100, height: 100)
    private let tileContainerView = UIView()
    private let tileColors: [UIColor] = [.red, .blue, .orange, .yellow, .green, .white]
    
    override func layoutSubviews() {
        tileContainerView.center = self.center
    }
    
    func addTiles() {
        tileContainerView.frame = CGRect(origin: .zero, size: CGSize(width: tileSize.width * CGFloat(tilesPerRow),
                                                                     height: tileSize.height * CGFloat(tilesPerRow)))
        for row in 0..<tilesPerRow {
            for col in 0..<tilesPerRow {
                let frame = CGRect(
                    x: CGFloat(col) * tileSize.width,
                    y: CGFloat(row) * tileSize.width,
                    width: tileSize.width,
                    height: tileSize.height
                )
                let tileView = UIView(frame: frame)
                tileView.backgroundColor = tileColors.randomElement()
                tileContainerView.addSubview(tileView)
            }
        }
        addSubview(tileContainerView)
        tileContainerView.center = self.center
    }
    
    func showWinningAnimation() {
        for subview in tileContainerView.subviews {

            let confettiEmitter = CAEmitterLayer()
            confettiEmitter.emitterShape = .rectangle
            confettiEmitter.emitterPosition = subview.center
            confettiEmitter.emitterSize = subview.bounds.size

            var confettiCells = [CAEmitterCell]()
            for color in self.tileColors {
                confettiCells.append(self.confettiWithColor(color: color))
            }

            confettiEmitter.emitterCells = confettiCells
            subview.layer.addSublayer(confettiEmitter)

            UIView.animate(withDuration: .random(in: 0...1)) {
                 subview.alpha = 0
            } completion: { _ in
                DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                    confettiEmitter.removeFromSuperlayer()
                }
            }
        }
    }

    private func confettiWithColor(color: UIColor) -> CAEmitterCell {
        let confetti = CAEmitterCell()
        confetti.color = color.cgColor
        confetti.birthRate = 5.0
        confetti.lifetime = 5.0
        confetti.velocity = 150
        confetti.emissionLongitude = CGFloat.pi
        confetti.emissionRange = CGFloat.pi * 2
        confetti.spin = 3.5
        confetti.spinRange = 1
        confetti.scaleRange = 0.5
        confetti.scaleSpeed = -0.1
        confetti.contents = UIImage.imageWithColor(.white, size: CGSize(width: 5, height: 5)).cgImage
        return confetti
    }
}

extension UIImage {
    static func imageWithColor(_ color: UIColor, size: CGSize) -> UIImage {
        let rect = CGRect(origin: .zero, size: size)
        UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
        color.setFill()
        UIRectFill(rect)
        let image = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()
        return image
    }
}
0

There are 0 best solutions below