Last active
December 5, 2021 21:28
-
-
Save mbernson/963fe7b17040a9d1ac74d40a856c6213 to your computer and use it in GitHub Desktop.
A view for your iOS app to draw some falling snow on top of your content. :)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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