Skip to content

Instantly share code, notes, and snippets.

@maxmzd
Created February 20, 2023 20:53
Show Gist options
  • Save maxmzd/21a4b25b4c80b9c3abcdb3bbe17091a8 to your computer and use it in GitHub Desktop.
Save maxmzd/21a4b25b4c80b9c3abcdb3bbe17091a8 to your computer and use it in GitHub Desktop.
Rowen
import UIKit
class ViewController: UIViewController {
var emitTimer: Timer?
var lastTouchPoints = [UITouch: CGPoint]()
var symbolLayers = [CAShapeLayer]()
let neonColors: [UIColor] = [
UIColor(red: 1.00, green: 0.30, blue: 0.69, alpha: 1.00), // neon pink
UIColor(red: 0.75, green: 1.00, blue: 0.00, alpha: 1.00), // neon green
UIColor(red: 1.00, green: 0.82, blue: 0.20, alpha: 1.00), // neon yellow
UIColor(red: 0.00, green: 0.83, blue: 1.00, alpha: 1.00), // neon blue
UIColor(red: 1.00, green: 0.55, blue: 0.00, alpha: 1.00), // neon orange
UIColor(red: 0.89, green: 0.00, blue: 1.00, alpha: 1.00) // neon purple
]
override func viewDidLoad() {
super.viewDidLoad()
view.isMultipleTouchEnabled = true
// Other setup code...
}
func emitImages(forTouches touches: Set<UITouch>) {
for touch in touches {
let touchPoint = touch.location(in: view)
let color = neonColors.randomElement()!
let symbolLayer = CAShapeLayer()
symbolLayer.path = UIBezierPath(rect: CGRect(x: 0, y: 0, width: 20, height: 20)).cgPath
symbolLayer.fillColor = color.cgColor
symbolLayer.frame = CGRect(x: 0, y: 0, width: 20, height: 20)
symbolLayer.position = touchPoint
symbolLayer.opacity = 1
symbolLayers.append(symbolLayer)
view.layer.addSublayer(symbolLayer)
let animation = CABasicAnimation(keyPath: "position")
animation.fromValue = NSValue(cgPoint: touchPoint)
animation.toValue = NSValue(cgPoint: randomPointAwayFromTouchPoint(touchPoint))
animation.duration = 1
animation.timingFunction = CAMediaTimingFunction(name: .easeOut)
animation.fillMode = .forwards
animation.isRemovedOnCompletion = false
symbolLayer.add(animation, forKey: "position")
let fadeOutAnimation = CABasicAnimation(keyPath: "opacity")
fadeOutAnimation.fromValue = 1
fadeOutAnimation.toValue = 0
fadeOutAnimation.duration = 0.5
fadeOutAnimation.beginTime = CACurrentMediaTime() + 0.5
fadeOutAnimation.fillMode = .forwards
fadeOutAnimation.isRemovedOnCompletion = false
symbolLayer.add(fadeOutAnimation, forKey: "opacity")
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
symbolLayer.removeFromSuperlayer()
if let index = self.symbolLayers.firstIndex(of: symbolLayer) {
self.symbolLayers.remove(at: index)
}
}
// Check if touch is still in progress, and if so, emit more images in random directions
if touch.phase != .ended && touch.phase != .cancelled {
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
if touch.phase != .ended && touch.phase != .cancelled {
self.emitImages(forTouches: Set([touch]))
}
}
}
}
}
var isTouchesMoving: Bool = false
func randomPointAwayFromTouchPoint(_ touchPoint: CGPoint) -> CGPoint {
let x = CGFloat.random(in: touchPoint.x - 100 ... touchPoint.x + 100)
let y = CGFloat.random(in: touchPoint.y - 100 ... touchPoint.y + 100)
return CGPoint(x: x, y: y)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
isTouchesMoving = false
emitImages(forTouches: touches)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
isTouchesMoving = true
emitImages(forTouches: touches)
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
isTouchesMoving = false
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
isTouchesMoving = false
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment