Как мы можем реализовать 3D-сенсор, чтобы проверить, удаляет ли пользователь на UIView
или нажимает на UIView
?
Есть ли способ сделать это с помощью UIGestureRecognize
или только с помощью UITouch
?
Как мы можем реализовать 3D-сенсор, чтобы проверить, удаляет ли пользователь на UIView
или нажимает на UIView
?
Есть ли способ сделать это с помощью UIGestureRecognize
или только с помощью UITouch
?
Вы можете сделать это без специального распознавателя жестов. Вам не нужно настраивать метод touchesEnded и touchhesBegan, но просто прикасаетсяМодуль для получения правильных значений. получение силы uitouch от начала/окончания вернет странные значения.
UITouch *touch = [touches anyObject];
CGFloat maximumPossibleForce = touch.maximumPossibleForce;
CGFloat force = touch.force;
CGFloat normalizedForce = force/maximumPossibleForce;
тогда установите порог силы и сравните нормализованныйForce с этим порогом (0.75 мне кажется штрафом).
Свойства 3D Touch доступны в UITouch
объектах.
Вы можете получить эти штрихи, переопределив методы UIView
touchesBegan:
и touchesMoved:
. Не уверен, что вы видите в touchesEnded:
.
Если вы хотите создать новые распознаватели жестов, у вас есть полный доступ к UITouch
es, как показано в UIGestureRecognizerSubclass
.
Я не уверен, как вы можете использовать свойства 3D-касания в традиционном UIGestureRecognizer
. Возможно, с помощью метода UIGestureRecognizerDelegate
протокола gestureRecognizer:shouldReceiveTouch:
.
То, как я это делаю, заключается в использовании комбинации UITapGestureRecognizer (предоставляется Apple) и DFContinuousForceTouchGestureRecognizer (предоставлено мной).
DFContinuousForceTouchGestureRecognizer
хорош, потому что он постоянно обновляет изменения давления, поэтому вы можете делать такие вещи, как увеличение вида, поскольку пользователь меняет свое давление на него, в отличие от одного события. Если вам просто нужно одно событие, вы можете игнорировать eveything в 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];
}
Селектор реализаций для жестов tap
#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];
}
Обратите внимание, что это будет полезно только на устройстве, поддерживающем force touch.
Также вы не должны добавлять DFContinuousForceTouchGestureRecognizer
к представлению, если вы работаете на iOS 8 или ниже, поскольку он использует новое свойство force
на UITouch
, доступное только в iOS 9.
Если вы добавите это на iOS 8, он потерпит крах, поэтому условно добавьте этот распознаватель на основе версии iOS, на которой вы работаете, если вы поддерживаете версии старше IOS 9.
Я создал UIGestureRecognizer, который эмулирует поведение приложения Apple Mail. При 3D-касании он начинается с короткого одиночного импульса, а затем дополнительного вторичного действия (hardTarget) и импульса, вызванного жестким нажатием вскоре после первоначального нажатия.
Адаптировано из https://github.com/FlexMonkey/DeepPressGestureRecognizer
Изменения:
Примечание. Я добавил недокументированный системный звук k_PeakSoundID, но не стесняйтесь отключить его, если вам неудобно использовать константу за пределами документированного диапазона. Я использую системные звуки с неопределенными константами в течение многих лет, но вам предлагается отключить вибрационные импульсы, используя свойство vibrateOnDeepPress.
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)
}
}
}