Skip to content

Instantly share code, notes, and snippets.

@mbernson
Last active December 5, 2021 21:28
Embed
What would you like to do?
A view for your iOS app to draw some falling snow on top of your content. :)
//
// Snow.swift
//
// Created by Mathijs Bernson on 05/12/2021.
//
import UIKit
import SwiftUI
/// A transparent view that displays falling snow on top of itself.
struct Snow: UIViewRepresentable {
func makeUIView(context: Context) -> UIView {
SnowView(frame: .zero)
}
func updateUIView(_ view: UIView, context: Context) { }
}
/// A transparent view that displays falling snow on top of itself.
class SnowView: UIView {
private let emitterLayer = CAEmitterLayer()
private var snowflake: CGImage? {
let size = CGSize(width: 40, height: 40)
let renderer = UIGraphicsImageRenderer(size: size)
let img = renderer.image { ctx in
let emoji = NSAttributedString(string: "❄️", attributes: [
.font: UIFont.systemFont(ofSize: 40)
])
emoji.draw(in: CGRect(origin: .zero, size: size))
}
return img.cgImage
}
override init(frame: CGRect) {
super.init(frame: frame)
isUserInteractionEnabled = false
let cell = CAEmitterCell()
cell.birthRate = 30
cell.lifetime = 20
cell.velocity = 50
cell.scale = 0.1
cell.emissionLongitude = CGFloat.pi
cell.emissionRange = CGFloat.pi / 2.0
cell.contents = snowflake
cell.spinRange = 5
emitterLayer.emitterCells = [cell]
emitterLayer.emitterShape = .line
layer.addSublayer(emitterLayer)
updateEmitterPosition()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func updateEmitterPosition() {
emitterLayer.frame = frame
emitterLayer.emitterSize = CGSize(width: frame.width * 2, height: 0)
emitterLayer.emitterPosition = CGPoint(x: frame.width / 2, y: frame.minY)
}
override func layoutSubviews() {
super.layoutSubviews()
updateEmitterPosition()
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let view = super.hitTest(point, with: event)
return view == self ? nil : view
}
}
struct Snow_Previews: PreviewProvider {
static var previews: some View {
Snow()
.ignoresSafeArea()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment