Skip to content

Instantly share code, notes, and snippets.

@shaps80
Last active March 21, 2024 18:44
Show Gist options
  • Save shaps80/641b61165fd5f8bd497e40f4d16d69be to your computer and use it in GitHub Desktop.
Save shaps80/641b61165fd5f8bd497e40f4d16d69be to your computer and use it in GitHub Desktop.
Distance travelled after decelerating to zero velocity at a constant rate. The included Playground file shows how you can use it with a pan gesture as an example.
public extension CGPoint {
// The target points after decelerating to 0 velocity at a constant rate
func target(initialVelocity: CGPoint, decelerationRate: CGFloat = UIScrollView.DecelerationRate.normal.rawValue) -> CGPoint {
let x = self.x + self.x.target(initialVelocity: initialVelocity.x, decelerationRate: decelerationRate)
let y = self.y + self.y.target(initialVelocity: initialVelocity.y, decelerationRate: decelerationRate)
return CGPoint(x: x, y: y)
}
}
fileprivate extension CGFloat {
func target(initialVelocity: CGFloat, decelerationRate: CGFloat = UIScrollView.DecelerationRate.normal.rawValue) -> CGFloat {
return (initialVelocity / 1000.0) * decelerationRate / (1.0 - decelerationRate)
}
}
//: A UIKit based Playground for presenting user interface
import UIKit
import PlaygroundSupport
final class ViewController: UIViewController {
private var gesture: UIPanGestureRecognizer!
private let box = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let gesture = UIPanGestureRecognizer(target: self, action: #selector(handlePan(gesture:)))
box.addGestureRecognizer(gesture)
box.center = view.center
box.layer.cornerRadius = 8
box.backgroundColor = .red
view.addSubview(box)
view.backgroundColor = .white
}
@objc private func handlePan(gesture: UIPanGestureRecognizer) {
let translation = gesture.translation(in: gesture.view)
switch gesture.state {
case .began, .changed:
box.center.x += translation.x
box.center.y += translation.y
case .ended:
let velocity = gesture.velocity(in: gesture.view)
let target = translation.target(initialVelocity: velocity)
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.9, initialSpringVelocity: 1, options: [], animations: {
self.box.center.x += target.x
self.box.center.y += target.y
}, completion: nil)
default: break
}
gesture.setTranslation(.zero, in: gesture.view)
}
}
PlaygroundPage.current.liveView = ViewController()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment