Created
July 25, 2021 14:11
-
-
Save cedricbahirwe/eb7cca51c221ee617ec68b2d9a462639 to your computer and use it in GitHub Desktop.
A simple prototype using `UIDynamicAnimator`
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
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