3D touch/Force 触摸实现
3D touch/Force touch implementation
我们如何实现 3D 触摸来检查用户是点击 UIView
还是强制触摸 UIView
?
有没有办法用 UIGestureRecognize
或只用 UITouch
来做到这一点?
3D Touch 属性are available on UITouch
objects。
您可以通过覆盖 UIView
的 touchesBegan:
和 touchesMoved:
方法来获得这些接触。还不确定您在 touchesEnded:
中看到了什么。
如果您愿意创建新的手势识别器,您可以完全访问 UIGestureRecognizerSubclass
中公开的 UITouch
es。
我不确定您如何在传统 UIGestureRecognizer
中使用 3D 触摸属性。也许通过 UIGestureRecognizerDelegate
协议的 gestureRecognizer:shouldReceiveTouch:
方法。
我这样做的方法是使用UITapGestureRecognizer (provided by Apple) and a DFContinuousForceTouchGestureRecognizer(由我提供)的组合。
DFContinuousForceTouchGestureRecognizer
很好,因为它提供有关压力变化的连续更新,因此您可以做一些事情,例如在用户改变对它施加的压力时增加视图,而不是单个事件。如果你只想要一个事件,你可以忽略 DFContinuousForceTouchDelegate
中的所有内容,除了 - (void) forceTouchRecognized
回调。
https://github.com/foggzilla/DFContinuousForceTouchGestureRecognizer
您可以在支持强制按下的设备上下载此应用和 运行 示例应用,看看它的感觉。
在您的 UIViewController
中执行以下操作:
- (void)viewDidLoad {
[super viewDidLoad];
_forceTouchRecognizer = [[DFContinuousForceTouchGestureRecognizer alloc] init];
_forceTouchRecognizer.forceTouchDelegate = self;
//here to demonstrate how this works alonside a tap gesture recognizer
_tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapped:)];
[self.imageView addGestureRecognizer:_tapGestureRecognizer];
[self.imageView addGestureRecognizer:_forceTouchRecognizer];
}
实现点击手势的选择器
#pragma UITapGestureRecognizer selector
- (void)tapped:(id)sender {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[[[UIAlertView alloc] initWithTitle:@"Tap" message:@"YEAH!!" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show];
});
}
实现强制触摸的委托协议:
#pragma DFContinuousForceTouchDelegate
- (void)forceTouchRecognized:(DFContinuousForceTouchGestureRecognizer *)recognizer {
self.imageView.transform = CGAffineTransformIdentity;
[self.imageView setNeedsDisplay];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[[[UIAlertView alloc] initWithTitle:@"Force Touch" message:@"YEAH!!" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show];
});
}
- (void)forceTouchRecognizer:(DFContinuousForceTouchGestureRecognizer *)recognizer didStartWithForce:(CGFloat)force maxForce:(CGFloat)maxForce {
CGFloat transformDelta = 1.0f + ((force/maxForce) / 3.0f);
self.imageView.transform = CGAffineTransformMakeScale(transformDelta, transformDelta);
[self.imageView setNeedsDisplay];
}
- (void) forceTouchRecognizer:(DFContinuousForceTouchGestureRecognizer *)recognizer didMoveWithForce:(CGFloat)force maxForce:(CGFloat)maxForce {
CGFloat transformDelta = 1.0f + ((force/maxForce) / 3.0f);
self.imageView.transform = CGAffineTransformMakeScale(transformDelta, transformDelta);
[self.imageView setNeedsDisplay];
}
- (void)forceTouchRecognizer:(DFContinuousForceTouchGestureRecognizer *)recognizer didCancelWithForce:(CGFloat)force maxForce:(CGFloat)maxForce {
self.imageView.transform = CGAffineTransformIdentity;
[self.imageView setNeedsDisplay];
}
- (void)forceTouchRecognizer:(DFContinuousForceTouchGestureRecognizer *)recognizer didEndWithForce:(CGFloat)force maxForce:(CGFloat)maxForce {
self.imageView.transform = CGAffineTransformIdentity;
[self.imageView setNeedsDisplay];
}
- (void)forceTouchDidTimeout:(DFContinuousForceTouchGestureRecognizer *)recognizer {
self.imageView.transform = CGAffineTransformIdentity;
[self.imageView setNeedsDisplay];
}
请注意,这仅在支持强制触摸的设备上有用。
此外,如果您 运行 正在使用 iOS 8 或以下,则不应将 DFContinuousForceTouchGestureRecognizer
添加到视图中,因为它使用新的 force
属性 在 UITouch
上仅在 iOS 9.
中可用
如果你在 iOS 8 上添加它会崩溃,所以如果你支持的版本早于iOS9.
你可以在没有指定手势识别器的情况下做到这一点。您无需调整 touchesEnded 和 touchesBegan 方法,只需调整 touchesMoved 即可获得正确的值。从 began/ended 获得 uitouch 的力将 return 奇怪的值。
UITouch *touch = [touches anyObject];
CGFloat maximumPossibleForce = touch.maximumPossibleForce;
CGFloat force = touch.force;
CGFloat normalizedForce = force/maximumPossibleForce;
然后,设置力阈值并将 normalizedForce 与该阈值进行比较(0.75 对我来说似乎很好)。
我创建了一个模拟 Apple Mail 应用程序行为的 UIGestureRecognizer。在 3D 触摸时,它从一个短的单脉冲振动开始,然后是一个可选的辅助动作 (hardTarget) 和在初始按下后不久通过用力按压调用的脉冲。
改编自https://github.com/FlexMonkey/DeepPressGestureRecognizer
变化:
- 3D 触摸振动脉冲类似于 iOS 系统行为
- 触摸必须出现才能结束,例如 Apple 邮件应用程序
- 阈值默认为系统默认级别
- 硬触摸会触发 hardAction 调用,例如邮件应用程序
注意:我添加了未记录的系统声音 k_PeakSoundID,但如果您对使用超出记录范围的常量感到不舒服,请随时将其关闭 。多年来我一直在使用具有未公开常数的系统声音,但欢迎您使用 vibrateOnDeepPress 属性 关闭振动脉冲。
import UIKit
import UIKit.UIGestureRecognizerSubclass
import AudioToolbox
class DeepPressGestureRecognizer: UIGestureRecognizer {
var vibrateOnDeepPress = true
var threshold: CGFloat = 0.75
var hardTriggerMinTime: TimeInterval = 0.5
var onDeepPress: (() -> Void)?
private var deepPressed: Bool = false {
didSet {
if (deepPressed && deepPressed != oldValue) {
onDeepPress?()
}
}
}
private var deepPressedAt: TimeInterval = 0
private var k_PeakSoundID: UInt32 = 1519
private var hardAction: Selector?
private var target: AnyObject?
required init(target: AnyObject?, action: Selector, hardAction: Selector? = nil, threshold: CGFloat = 0.75) {
self.target = target
self.hardAction = hardAction
self.threshold = threshold
super.init(target: target, action: action)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
if let touch = touches.first {
handle(touch: touch)
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
if let touch = touches.first {
handle(touch: touch)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesEnded(touches, with: event)
state = deepPressed ? UIGestureRecognizerState.ended : UIGestureRecognizerState.failed
deepPressed = false
}
private func handle(touch: UITouch) {
guard let _ = view, touch.force != 0 && touch.maximumPossibleForce != 0 else {
return
}
let forcePercentage = (touch.force / touch.maximumPossibleForce)
let currentTime = Date.timeIntervalSinceReferenceDate
if !deepPressed && forcePercentage >= threshold {
state = UIGestureRecognizerState.began
if vibrateOnDeepPress {
AudioServicesPlaySystemSound(k_PeakSoundID)
}
deepPressedAt = Date.timeIntervalSinceReferenceDate
deepPressed = true
} else if deepPressed && forcePercentage <= 0 {
endGesture()
} else if deepPressed && currentTime - deepPressedAt > hardTriggerMinTime && forcePercentage == 1.0 {
endGesture()
if vibrateOnDeepPress {
AudioServicesPlaySystemSound(k_PeakSoundID)
}
//fire hard press
if let hardAction = self.hardAction, let target = self.target {
_ = target.perform(hardAction, with: self)
}
}
}
func endGesture() {
state = UIGestureRecognizerState.ended
deepPressed = false
}
}
// MARK: DeepPressable protocol extension
protocol DeepPressable {
var gestureRecognizers: [UIGestureRecognizer]? {get set}
func addGestureRecognizer(gestureRecognizer: UIGestureRecognizer)
func removeGestureRecognizer(gestureRecognizer: UIGestureRecognizer)
func setDeepPressAction(target: AnyObject, action: Selector)
func removeDeepPressAction()
}
extension DeepPressable {
func setDeepPressAction(target: AnyObject, action: Selector) {
let deepPressGestureRecognizer = DeepPressGestureRecognizer(target: target, action: action, threshold: 0.75)
self.addGestureRecognizer(gestureRecognizer: deepPressGestureRecognizer)
}
func removeDeepPressAction() {
guard let gestureRecognizers = gestureRecognizers else { return }
for recogniser in gestureRecognizers where recogniser is DeepPressGestureRecognizer {
removeGestureRecognizer(gestureRecognizer: recogniser)
}
}
}
使用 Swift 4.2 和 iOS 12,解决您的问题的一种可能方法是创建一个 UIGestureRecognizer
的自定义子类来处理 Force Touch,然后将其添加到您的视图中到 UITapGestureRecognizer
。以下完整代码展示了如何实现它:
ViewController.swift
import UIKit
class ViewController: UIViewController {
let redView = UIView()
lazy var tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(tapHandler))
lazy var forceTouchGestureRecognizer = ForceTouchGestureRecognizer(target: self, action: #selector(forceTouchHandler))
override func viewDidLoad() {
super.viewDidLoad()
redView.backgroundColor = .red
redView.addGestureRecognizer(tapGestureRecognizer)
view.addSubview(redView)
redView.translatesAutoresizingMaskIntoConstraints = false
redView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
redView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
redView.widthAnchor.constraint(equalToConstant: 100).isActive = true
redView.heightAnchor.constraint(equalToConstant: 100).isActive = true
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
if traitCollection.forceTouchCapability == UIForceTouchCapability.available {
redView.addGestureRecognizer(forceTouchGestureRecognizer)
} else {
// When force touch is not available, remove force touch gesture recognizer.
// Also implement a fallback if necessary (e.g. a long press gesture recognizer)
redView.removeGestureRecognizer(forceTouchGestureRecognizer)
}
}
@objc func tapHandler(_ sender: UITapGestureRecognizer) {
print("Tap triggered")
}
@objc func forceTouchHandler(_ sender: ForceTouchGestureRecognizer) {
UINotificationFeedbackGenerator().notificationOccurred(.success)
print("Force touch triggered")
}
}
ForceTouchGestureRecognizer.swift
import UIKit.UIGestureRecognizerSubclass
@available(iOS 9.0, *)
final class ForceTouchGestureRecognizer: UIGestureRecognizer {
private let threshold: CGFloat = 0.75
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesBegan(touches, with: event)
if let touch = touches.first {
handleTouch(touch)
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesMoved(touches, with: event)
if let touch = touches.first {
handleTouch(touch)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesEnded(touches, with: event)
state = UIGestureRecognizer.State.failed
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesCancelled(touches, with: event)
state = UIGestureRecognizer.State.failed
}
private func handleTouch(_ touch: UITouch) {
guard touch.force != 0 && touch.maximumPossibleForce != 0 else { return }
if touch.force / touch.maximumPossibleForce >= threshold {
state = UIGestureRecognizer.State.recognized
}
}
}
来源:
我们如何实现 3D 触摸来检查用户是点击 UIView
还是强制触摸 UIView
?
有没有办法用 UIGestureRecognize
或只用 UITouch
来做到这一点?
3D Touch 属性are available on UITouch
objects。
您可以通过覆盖 UIView
的 touchesBegan:
和 touchesMoved:
方法来获得这些接触。还不确定您在 touchesEnded:
中看到了什么。
如果您愿意创建新的手势识别器,您可以完全访问 UIGestureRecognizerSubclass
中公开的 UITouch
es。
我不确定您如何在传统 UIGestureRecognizer
中使用 3D 触摸属性。也许通过 UIGestureRecognizerDelegate
协议的 gestureRecognizer:shouldReceiveTouch:
方法。
我这样做的方法是使用UITapGestureRecognizer (provided by Apple) and a DFContinuousForceTouchGestureRecognizer(由我提供)的组合。
DFContinuousForceTouchGestureRecognizer
很好,因为它提供有关压力变化的连续更新,因此您可以做一些事情,例如在用户改变对它施加的压力时增加视图,而不是单个事件。如果你只想要一个事件,你可以忽略 DFContinuousForceTouchDelegate
中的所有内容,除了 - (void) forceTouchRecognized
回调。
https://github.com/foggzilla/DFContinuousForceTouchGestureRecognizer
您可以在支持强制按下的设备上下载此应用和 运行 示例应用,看看它的感觉。
在您的 UIViewController
中执行以下操作:
- (void)viewDidLoad {
[super viewDidLoad];
_forceTouchRecognizer = [[DFContinuousForceTouchGestureRecognizer alloc] init];
_forceTouchRecognizer.forceTouchDelegate = self;
//here to demonstrate how this works alonside a tap gesture recognizer
_tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapped:)];
[self.imageView addGestureRecognizer:_tapGestureRecognizer];
[self.imageView addGestureRecognizer:_forceTouchRecognizer];
}
实现点击手势的选择器
#pragma UITapGestureRecognizer selector
- (void)tapped:(id)sender {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[[[UIAlertView alloc] initWithTitle:@"Tap" message:@"YEAH!!" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show];
});
}
实现强制触摸的委托协议:
#pragma DFContinuousForceTouchDelegate
- (void)forceTouchRecognized:(DFContinuousForceTouchGestureRecognizer *)recognizer {
self.imageView.transform = CGAffineTransformIdentity;
[self.imageView setNeedsDisplay];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[[[UIAlertView alloc] initWithTitle:@"Force Touch" message:@"YEAH!!" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show];
});
}
- (void)forceTouchRecognizer:(DFContinuousForceTouchGestureRecognizer *)recognizer didStartWithForce:(CGFloat)force maxForce:(CGFloat)maxForce {
CGFloat transformDelta = 1.0f + ((force/maxForce) / 3.0f);
self.imageView.transform = CGAffineTransformMakeScale(transformDelta, transformDelta);
[self.imageView setNeedsDisplay];
}
- (void) forceTouchRecognizer:(DFContinuousForceTouchGestureRecognizer *)recognizer didMoveWithForce:(CGFloat)force maxForce:(CGFloat)maxForce {
CGFloat transformDelta = 1.0f + ((force/maxForce) / 3.0f);
self.imageView.transform = CGAffineTransformMakeScale(transformDelta, transformDelta);
[self.imageView setNeedsDisplay];
}
- (void)forceTouchRecognizer:(DFContinuousForceTouchGestureRecognizer *)recognizer didCancelWithForce:(CGFloat)force maxForce:(CGFloat)maxForce {
self.imageView.transform = CGAffineTransformIdentity;
[self.imageView setNeedsDisplay];
}
- (void)forceTouchRecognizer:(DFContinuousForceTouchGestureRecognizer *)recognizer didEndWithForce:(CGFloat)force maxForce:(CGFloat)maxForce {
self.imageView.transform = CGAffineTransformIdentity;
[self.imageView setNeedsDisplay];
}
- (void)forceTouchDidTimeout:(DFContinuousForceTouchGestureRecognizer *)recognizer {
self.imageView.transform = CGAffineTransformIdentity;
[self.imageView setNeedsDisplay];
}
请注意,这仅在支持强制触摸的设备上有用。
此外,如果您 运行 正在使用 iOS 8 或以下,则不应将 DFContinuousForceTouchGestureRecognizer
添加到视图中,因为它使用新的 force
属性 在 UITouch
上仅在 iOS 9.
如果你在 iOS 8 上添加它会崩溃,所以如果你支持的版本早于iOS9.
你可以在没有指定手势识别器的情况下做到这一点。您无需调整 touchesEnded 和 touchesBegan 方法,只需调整 touchesMoved 即可获得正确的值。从 began/ended 获得 uitouch 的力将 return 奇怪的值。
UITouch *touch = [touches anyObject];
CGFloat maximumPossibleForce = touch.maximumPossibleForce;
CGFloat force = touch.force;
CGFloat normalizedForce = force/maximumPossibleForce;
然后,设置力阈值并将 normalizedForce 与该阈值进行比较(0.75 对我来说似乎很好)。
我创建了一个模拟 Apple Mail 应用程序行为的 UIGestureRecognizer。在 3D 触摸时,它从一个短的单脉冲振动开始,然后是一个可选的辅助动作 (hardTarget) 和在初始按下后不久通过用力按压调用的脉冲。
改编自https://github.com/FlexMonkey/DeepPressGestureRecognizer
变化:
- 3D 触摸振动脉冲类似于 iOS 系统行为
- 触摸必须出现才能结束,例如 Apple 邮件应用程序
- 阈值默认为系统默认级别
- 硬触摸会触发 hardAction 调用,例如邮件应用程序
注意:我添加了未记录的系统声音 k_PeakSoundID,但如果您对使用超出记录范围的常量感到不舒服,请随时将其关闭 。多年来我一直在使用具有未公开常数的系统声音,但欢迎您使用 vibrateOnDeepPress 属性 关闭振动脉冲。
import UIKit
import UIKit.UIGestureRecognizerSubclass
import AudioToolbox
class DeepPressGestureRecognizer: UIGestureRecognizer {
var vibrateOnDeepPress = true
var threshold: CGFloat = 0.75
var hardTriggerMinTime: TimeInterval = 0.5
var onDeepPress: (() -> Void)?
private var deepPressed: Bool = false {
didSet {
if (deepPressed && deepPressed != oldValue) {
onDeepPress?()
}
}
}
private var deepPressedAt: TimeInterval = 0
private var k_PeakSoundID: UInt32 = 1519
private var hardAction: Selector?
private var target: AnyObject?
required init(target: AnyObject?, action: Selector, hardAction: Selector? = nil, threshold: CGFloat = 0.75) {
self.target = target
self.hardAction = hardAction
self.threshold = threshold
super.init(target: target, action: action)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
if let touch = touches.first {
handle(touch: touch)
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
if let touch = touches.first {
handle(touch: touch)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesEnded(touches, with: event)
state = deepPressed ? UIGestureRecognizerState.ended : UIGestureRecognizerState.failed
deepPressed = false
}
private func handle(touch: UITouch) {
guard let _ = view, touch.force != 0 && touch.maximumPossibleForce != 0 else {
return
}
let forcePercentage = (touch.force / touch.maximumPossibleForce)
let currentTime = Date.timeIntervalSinceReferenceDate
if !deepPressed && forcePercentage >= threshold {
state = UIGestureRecognizerState.began
if vibrateOnDeepPress {
AudioServicesPlaySystemSound(k_PeakSoundID)
}
deepPressedAt = Date.timeIntervalSinceReferenceDate
deepPressed = true
} else if deepPressed && forcePercentage <= 0 {
endGesture()
} else if deepPressed && currentTime - deepPressedAt > hardTriggerMinTime && forcePercentage == 1.0 {
endGesture()
if vibrateOnDeepPress {
AudioServicesPlaySystemSound(k_PeakSoundID)
}
//fire hard press
if let hardAction = self.hardAction, let target = self.target {
_ = target.perform(hardAction, with: self)
}
}
}
func endGesture() {
state = UIGestureRecognizerState.ended
deepPressed = false
}
}
// MARK: DeepPressable protocol extension
protocol DeepPressable {
var gestureRecognizers: [UIGestureRecognizer]? {get set}
func addGestureRecognizer(gestureRecognizer: UIGestureRecognizer)
func removeGestureRecognizer(gestureRecognizer: UIGestureRecognizer)
func setDeepPressAction(target: AnyObject, action: Selector)
func removeDeepPressAction()
}
extension DeepPressable {
func setDeepPressAction(target: AnyObject, action: Selector) {
let deepPressGestureRecognizer = DeepPressGestureRecognizer(target: target, action: action, threshold: 0.75)
self.addGestureRecognizer(gestureRecognizer: deepPressGestureRecognizer)
}
func removeDeepPressAction() {
guard let gestureRecognizers = gestureRecognizers else { return }
for recogniser in gestureRecognizers where recogniser is DeepPressGestureRecognizer {
removeGestureRecognizer(gestureRecognizer: recogniser)
}
}
}
使用 Swift 4.2 和 iOS 12,解决您的问题的一种可能方法是创建一个 UIGestureRecognizer
的自定义子类来处理 Force Touch,然后将其添加到您的视图中到 UITapGestureRecognizer
。以下完整代码展示了如何实现它:
ViewController.swift
import UIKit
class ViewController: UIViewController {
let redView = UIView()
lazy var tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(tapHandler))
lazy var forceTouchGestureRecognizer = ForceTouchGestureRecognizer(target: self, action: #selector(forceTouchHandler))
override func viewDidLoad() {
super.viewDidLoad()
redView.backgroundColor = .red
redView.addGestureRecognizer(tapGestureRecognizer)
view.addSubview(redView)
redView.translatesAutoresizingMaskIntoConstraints = false
redView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
redView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
redView.widthAnchor.constraint(equalToConstant: 100).isActive = true
redView.heightAnchor.constraint(equalToConstant: 100).isActive = true
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
if traitCollection.forceTouchCapability == UIForceTouchCapability.available {
redView.addGestureRecognizer(forceTouchGestureRecognizer)
} else {
// When force touch is not available, remove force touch gesture recognizer.
// Also implement a fallback if necessary (e.g. a long press gesture recognizer)
redView.removeGestureRecognizer(forceTouchGestureRecognizer)
}
}
@objc func tapHandler(_ sender: UITapGestureRecognizer) {
print("Tap triggered")
}
@objc func forceTouchHandler(_ sender: ForceTouchGestureRecognizer) {
UINotificationFeedbackGenerator().notificationOccurred(.success)
print("Force touch triggered")
}
}
ForceTouchGestureRecognizer.swift
import UIKit.UIGestureRecognizerSubclass
@available(iOS 9.0, *)
final class ForceTouchGestureRecognizer: UIGestureRecognizer {
private let threshold: CGFloat = 0.75
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesBegan(touches, with: event)
if let touch = touches.first {
handleTouch(touch)
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesMoved(touches, with: event)
if let touch = touches.first {
handleTouch(touch)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesEnded(touches, with: event)
state = UIGestureRecognizer.State.failed
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesCancelled(touches, with: event)
state = UIGestureRecognizer.State.failed
}
private func handleTouch(_ touch: UITouch) {
guard touch.force != 0 && touch.maximumPossibleForce != 0 else { return }
if touch.force / touch.maximumPossibleForce >= threshold {
state = UIGestureRecognizer.State.recognized
}
}
}
来源: