Created
April 1, 2020 00:41
-
-
Save Coder-ACJHP/1e9521717ef1de4fe82b0ae528b38abf to your computer and use it in GitHub Desktop.
UIView includes floating hearts like facebook live streams animation
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
// | |
// 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