Skip to content

Instantly share code, notes, and snippets.

@cedricbahirwe
Created July 25, 2021 14:11
Show Gist options
  • Save cedricbahirwe/eb7cca51c221ee617ec68b2d9a462639 to your computer and use it in GitHub Desktop.
Save cedricbahirwe/eb7cca51c221ee617ec68b2d9a462639 to your computer and use it in GitHub Desktop.
A simple prototype using `UIDynamicAnimator`
class ViewController: UIViewController {
var animator: UIDynamicAnimator?
var origins: [(String, CGPoint)] = []
let range: ClosedRange<CGFloat> = 0.0...255.0
var message = "This is cedric trying to animate the components with gravity"
override func viewDidLoad() {
self.view.backgroundColor = UIColor.black
}
override func viewDidLayoutSubviews() {
startAnimation()
}
func startAnimation() {
guard animator == nil else { return }
// 1: split the message up into words
let words = message.components(separatedBy: " ")
// 2: create an empty array of labels
var labels = [UILabel]()
// 3: convert each word into a label
for (index, word) in words.enumerated() {
let label = UILabel()
label.font = UIFont.preferredFont(forTextStyle: .largeTitle)
// 4: position the labels one above the other
label.center = CGPoint(x: view.frame.midX/2, y: 50 + CGFloat(30 * index))
label.text = word
label.isUserInteractionEnabled = true
label.sizeToFit()
// Genrate Random color foreach label
label.textColor = UIColor(red: .random(in: range)/255, green: .random(in: range)/255, blue: .random(in: range)/255, alpha: 1)
// MARK: A. Store the original positions of each label before appling `UIDynamicAnimator`
self.origins.append((label.text!, label.center))
// MARK: Apply Pan Gesture to each label
addPanGesture(sender: label)
view.addSubview(label)
labels.append(label)
}
// 5: create a gravity behaviour for our labels
let gravity = UIGravityBehavior(items: labels)
animator = UIDynamicAnimator(referenceView: view)
animator?.addBehavior(gravity)
// 6: create a collision behavior for our labels
let collisions = UICollisionBehavior(items: labels)
// 7: give some boundaries for the collisions
collisions.translatesReferenceBoundsIntoBoundary = true
animator?.addBehavior(collisions)
}
func addPanGesture(sender: UILabel) {
let pan = UIPanGestureRecognizer(target: self, action: #selector(self.didPan(sender:)))
sender.addGestureRecognizer(pan)
}
// Animate each label with a pan gesture
@objc func didPan(sender: UIPanGestureRecognizer) {
let fileView = sender.view!
let translation = sender.translation(in: self.view)
switch sender.state {
case .began, .changed:
fileView.center = CGPoint(x: translation.x + fileView.center.x, y: translation.y + fileView.center.y)
sender.setTranslation(CGPoint.zero, in: self.view)
case .ended :
if fileView.isKind(of: UILabel.self) == true {
let label = fileView as! UILabel
if let element = self.origins.first(where: { $0.0 == label.text }) {
UIView.animate(withDuration: 0.6) {
fileView.center = element.1
UIView.animate(withDuration: 0.4, animations: {
fileView.transform = .identity
}, completion: {
completed in
self.origins.removeAll(where: { $0 == element })
self.animateLabels()
})
}
}
}
default:
break
}
}
// Set white color to the label when animation finishes
func animateLabels() {
if origins.isEmpty {
for subview in view.subviews {
if subview.isKind(of: UILabel.self) {
let label = subview as! UILabel
UIView.animate(withDuration: 0.4) {
label.textColor = .white
}
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment