3D触摸/强制触摸实施

我们如何实现3D触摸来检查用户是在UIView还是在UIView上触摸?

有没有办法做到这一点与UIGestureRecognize或只有与UITouch

你可以不用指定的手势识别器。 你不需要调整touchesEnded和touchesBegan方法,只需要touchesMoved来获得正确的值。 从开始/结束获得uitouch的力量将返回奇怪的值。

 UITouch *touch = [touches anyObject]; CGFloat maximumPossibleForce = touch.maximumPossibleForce; CGFloat force = touch.force; CGFloat normalizedForce = force/maximumPossibleForce; 

然后,设置一个力门槛和比较标准化的力量这个门槛(0.75似乎罚款给我)。

3D Touch属性在UITouch对象上可用 。

你可以通过覆盖UIViewtouchesBegan:touchesMoved:方法来获得这些触摸。 不知道你看到什么touchesEnded:但。

如果您愿意创build新的手势识别器,则可以完全访问在UIGestureRecognizerSubclass公开的UITouch es。

我不确定如何在传统的UIGestureRecognizer使用3D触摸属性。 也许通过UIGestureRecognizerDelegate协议的gestureRecognizer:shouldReceiveTouch:方法。

我这样做的方式是使用UITapGestureRecognizer (由Apple提供)和DFContinuousForceTouchGestureRecognizer (由我提供)的组合。

DFContinuousForceTouchGestureRecognizer是很好的,因为它提供了关于压力变化的连续更新,所以你可以做一些事情,比如在用户改变压力时增加视图,而不是单个事件。 如果你只想要一个事件,除了- (void) forceTouchRecognizedcallback之外,你可以忽略DFContinuousForceTouchDelegate中的DFContinuousForceTouchDelegate

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]; } 

实现点击手势的select器

 #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上运行, DFContinuousForceTouchGestureRecognizer添加到视图,因为它仅在iOS 9中使用UITouch上的新force属性。

如果你在iOS 8上添加它,它将会崩溃,所以如果你支持iOS 9之前的版本,有条件地添加这个识别器是基于你正在运行的iOS版本。

我创build了一个模拟Apple Mail应用程序的行为的UIGestureRecognizer。 在3D触摸的时候,它会从一个简单的单脉冲振动开始,然后是一个可选的次级动作(hardTarget)和脉冲,在初始按压后不久用硬按下。

改编自https://github.com/FlexMonkey/DeepPressGestureRecognizer

变化:

  • 3D触摸震动脉冲,如iOS系统的行为
  • 触摸必须拿出来,像苹果邮件应用程序
  • 阈值默认为系统默认级别
  • 硬触摸触发hardAction调用像邮件应用程序

注意:我添加了未logging的系统声音k_PeakSoundID,但如果使用超出logging范围的常量不舒服,则可以随意closures此function 。 我一直在使用未公开常量的系统声音多年,但欢迎您使用vibrateOnDeepPress属性closures振动脉冲。

 import AudioToolbox import UIKit.UIGestureRecognizerSubclass class DeepPressGestureRecognizer: UIGestureRecognizer { var vibrateOnDeepPress = true var threshold:CGFloat = 0.75 var hardTriggerMinTime:NSTimeInterval = 0.5 private var deepPressed: Bool = false private var deepPressedAt: NSTimeInterval = 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>, withEvent event: UIEvent) { if let touch = touches.first { handleTouch(touch) } } override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent) { if let touch = touches.first { handleTouch(touch) } } override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent) { super.touchesEnded(touches, withEvent: event) state = deepPressed ? UIGestureRecognizerState.Ended : UIGestureRecognizerState.Failed deepPressed = false } private func handleTouch(touch: UITouch) { guard let _ = view where touch.force != 0 && touch.maximumPossibleForce != 0 else { return } let forcePercentage = (touch.force / touch.maximumPossibleForce) let currentTime = NSDate.timeIntervalSinceReferenceDate() if !deepPressed && forcePercentage >= threshold { state = UIGestureRecognizerState.Began if vibrateOnDeepPress { AudioServicesPlaySystemSound(k_PeakSoundID) } deepPressedAt = NSDate.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.performSelector(hardAction, withObject: 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(deepPressGestureRecognizer) } func removeDeepPressAction() { guard let gestureRecognizers = gestureRecognizers else { return } for recogniser in gestureRecognizers where recogniser is DeepPressGestureRecognizer { removeGestureRecognizer(recogniser) } } }