Last active
July 3, 2019 09:10
-
-
Save ppth0608/843085bb765e9452b99e264201289218 to your computer and use it in GitHub Desktop.
How to use Force Touch Gesture to view
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
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) } | |
} | |
} |
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
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 | |
} | |
} |
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
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