Skip to content

Instantly share code, notes, and snippets.

@Coder-ACJHP
Created April 1, 2020 00:41
Show Gist options
  • Save Coder-ACJHP/1e9521717ef1de4fe82b0ae528b38abf to your computer and use it in GitHub Desktop.
Save Coder-ACJHP/1e9521717ef1de4fe82b0ae528b38abf to your computer and use it in GitHub Desktop.
UIView includes floating hearts like facebook live streams animation
//
// AnimationView.swift
// autofix
//
// Created by Onur Işık on 31.03.2020.
// Copyright © 2020 Coder ACJHP. All rights reserved.
//
import UIKit
class AnimationView: UIView, CAAnimationDelegate {
private var timer: Timer?
private var startPoint: CGPoint!
private var endPoint: CGPoint!
private var controlPoint1: CGPoint!
private var controlPoint2: CGPoint!
private let heartImage = UIImage(named: "animationHeartImage")
private var heartsStack: [UIImageView] = Array()
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = .white
self.startPoint = .init(x: frame.size.width / 2, y: frame.size.height)
}
required init?(coder: NSCoder) {
super.init(coder: coder)
self.backgroundColor = .white
}
fileprivate func createPath() -> UIBezierPath {
self.calculatePointsDepended()
let linePath = UIBezierPath()
linePath.move(to: self.startPoint)
linePath.addQuadCurve(to: self.endPoint, controlPoint: controlPoint1)
return linePath
}
fileprivate func calculatePointsDepended() {
let randomPosiX = CGFloat.random(in: 0 ... self.frame.size.width)
let randomPosiY = CGFloat.random(in: 0 ... self.frame.size.height)
self.endPoint = .init(x: randomPosiX, y: 0)
self.controlPoint1 = CGPoint(x: self.frame.size.width - randomPosiX,
y: self.frame.size.height - randomPosiY)
self.controlPoint2 = CGPoint(x: self.frame.size.width - randomPosiX,
y: self.frame.size.height - randomPosiY)
}
@objc
private func generateFloatingHearts() {
(0...2).forEach { (_) in
self.setupFloatingHearts()
}
}
fileprivate func setupFloatingHearts() {
let floatingIcon = UIImageView(image: heartImage)
let dimension = CGFloat.random(in: 10 ... 30)
floatingIcon.frame = .init(x: 0, y: 0, width: dimension, height: dimension)
addSubview(floatingIcon)
let duration = Double.random(in: 1.5 ... 3.0)
let positionAnimation = CAKeyframeAnimation(keyPath: "position")
positionAnimation.path = self.createPath().cgPath
positionAnimation.duration = duration
positionAnimation.fillMode = .forwards
positionAnimation.isRemovedOnCompletion = true
positionAnimation.timingFunction = CAMediaTimingFunction(name: .linear)
floatingIcon.layer.add(positionAnimation, forKey: nil)
let opacityAnimation = CABasicAnimation(keyPath: "opacity")
opacityAnimation.fromValue = 1.0
opacityAnimation.toValue = 0.0
opacityAnimation.duration = duration
opacityAnimation.delegate = self
opacityAnimation.isRemovedOnCompletion = true
floatingIcon.layer.add(opacityAnimation, forKey: nil)
heartsStack.append(floatingIcon)
}
func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
if flag {
for index in 0 ... 6 {
let view = heartsStack[index]
view.removeFromSuperview()
}
heartsStack.removeFirst(5)
}
}
public func startAnimation() {
timer = Timer(timeInterval: 0.4,
target: self,
selector: #selector(generateFloatingHearts),
userInfo: nil,
repeats: true)
RunLoop.main.add(timer!, forMode: .common)
}
public func stopAnimation() {
heartsStack.removeAll()
guard let timer = timer else { return }
timer.invalidate()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment