Skip to content

Instantly share code, notes, and snippets.

@ppth0608
Last active July 3, 2019 09:10
Show Gist options
  • Save ppth0608/843085bb765e9452b99e264201289218 to your computer and use it in GitHub Desktop.
Save ppth0608/843085bb765e9452b99e264201289218 to your computer and use it in GitHub Desktop.
How to use Force Touch Gesture to view
import UIKit
/// Protocol that must be concatenated for the convenient use of`ForceTouchGestureRecognizer`
protocol ForceTouchGestureConvertible: class {
var forceTouchGestureRecognizer: UIGestureRecognizer? { get set }
}
extension ForceTouchGestureConvertible where Self: UIView {
/// Add `ForceTouchGestureRecognizer` to UIView that conform `ForceTouchGestureConvertible` protocol
func addForceTouchGestoureRecognizer(callback: @escaping ForceTouchGestureRecognizer.Callback) {
guard UIScreen.main.traitCollection.forceTouchCapability == .available else {
return
}
let recognizer = ForceTouchGestureRecognizer(threshold: 0.75, callback: callback)
self.forceTouchGestureRecognizer = recognizer
addGestureRecognizer(recognizer)
}
/// Remove `ForceTouchGestureRecognizer`
func removeForceTouchGestoureRecognizer() {
forceTouchGestureRecognizer.map {
self.removeGestureRecognizer($0)
}
}
}
extension UIView: ForceTouchGestureConvertible { }
private var forceTouchGestureRecognizerKey: Void?
extension UIView {
var forceTouchGestureRecognizer: UIGestureRecognizer? {
get { return objc_getAssociatedObject(self, &forceTouchGestureRecognizerKey) as? UIGestureRecognizer }
set { objc_setAssociatedObject(self, &forceTouchGestureRecognizerKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) }
}
}
import UIKit
/// `ForceTouchGestureRecognizer` is a subclass of `UIGestureRecognizer` for recognize force touch action.
class ForceTouchGestureRecognizer: UIGestureRecognizer {
typealias Callback = (UIGestureRecognizer) -> Void
/// Pressed force when the view is pressed.
private(set) var force: CGFloat = 0
/// The percent visible threshold that triggers force touch recognized. (A value should between 0 ~ 1.)
private let threshold: CGFloat
/// Closure that when `UIGestureRecognizer.State` is changed.
private var callback: Callback
init(threshold: CGFloat, callback: @escaping Callback) {
self.threshold = threshold
self.callback = callback
super.init(target: nil, action: nil)
cancelsTouchesInView = false
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesBegan(touches, with: event)
handleTouches(.began, touches: touches)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesMoved(touches, with: event)
handleTouches(.changed, touches: touches)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesBegan(touches, with: event)
handleTouches(.cancelled, touches: touches)
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesCancelled(touches, with: event)
handleTouches(.cancelled, touches: touches)
}
/// handle and update `UIGestureRecognizer.State` when `UITouch` event is changed.
private func handleTouches(_ state: UIGestureRecognizer.State, touches: Set<UITouch>) {
guard let touch = touches.first else { return }
let force = touch.force / touch.maximumPossibleForce
self.state = state
switch state {
case .changed:
self.force = force
if force >= threshold {
cancelsTouchesInView = true
self.state = .ended
}
case .cancelled:
cancelsTouchesInView = true
default:
break
}
callback(self)
}
override func reset() {
super.reset()
force = 0
cancelsTouchesInView = false
}
}
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
setupForceTouchRecogizer()
}
func setupForceTouchRecogizer() {
button.removeForceTouchGestoureRecognizer()
switch UIScreen.main.traitCollection.forceTouchCapability {
case .available:
button.addForceTouchGestoureRecognizer(callback: handleForceTouchRecognizer)
case .unavailable, .unknown:
let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(handleForceTouchRecognizer))
button.forceTouchGestureRecognizer = longPressGesture
button.addGestureRecognizer(longPressGesture)
}
}
@objc func handleForceTouchRecognizer(gesture: UIGestureRecognizer) {
switch gesture {
case is ForceTouchGestureRecognizer where gesture.state == .ended,
is UILongPressGestureRecognizer where gesture.state == .began:
UIImpactFeedbackGenerator(style: .heavy).impactOccurred()
print("Triggers!!")
default: break
}
}
@IBAction func didTapButton(_ sender: UIButton) {
print("didTapButton")
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
setupForceTouchRecogizer()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment