Подтвердить что ты не робот

Реализация 3D touch/Force touch

Как мы можем реализовать 3D-сенсор, чтобы проверить, удаляет ли пользователь на UIView или нажимает на UIView?

Есть ли способ сделать это с помощью UIGestureRecognize или только с помощью UITouch?

4b9b3361

Ответ 1

Вы можете сделать это без специального распознавателя жестов. Вам не нужно настраивать метод touchesEnded и touchhesBegan, но просто прикасаетсяМодуль для получения правильных значений. получение силы uitouch от начала/окончания вернет странные значения.

UITouch *touch = [touches anyObject];

CGFloat maximumPossibleForce = touch.maximumPossibleForce;
CGFloat force = touch.force;
CGFloat normalizedForce = force/maximumPossibleForce;

тогда установите порог силы и сравните нормализованныйForce с этим порогом (0.75 мне кажется штрафом).

Ответ 2

Свойства 3D Touch доступны в UITouch объектах.

Вы можете получить эти штрихи, переопределив методы UIView touchesBegan: и touchesMoved:. Не уверен, что вы видите в touchesEnded:.

Если вы хотите создать новые распознаватели жестов, у вас есть полный доступ к UITouch es, как показано в UIGestureRecognizerSubclass.

Я не уверен, как вы можете использовать свойства 3D-касания в традиционном UIGestureRecognizer. Возможно, с помощью метода UIGestureRecognizerDelegate протокола gestureRecognizer:shouldReceiveTouch:.

Ответ 3

То, как я это делаю, заключается в использовании комбинации 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.

Ответ 4

Я создал UIGestureRecognizer, который эмулирует поведение приложения Apple Mail. При 3D-касании он начинается с короткого одиночного импульса, а затем дополнительного вторичного действия (hardTarget) и импульса, вызванного жестким нажатием вскоре после первоначального нажатия.

Адаптировано из https://github.com/FlexMonkey/DeepPressGestureRecognizer

Изменения:

  • 3D-сенсорные вибрационные импульсы, такие как поведение системы iOS
  • Прикосновение должно прийти к концу, например, Apple mail app
  • пороговое значение по умолчанию для системного уровня по умолчанию
  • Трудный контакт вызывает вызов hardAction, например, почтовое приложение.

Примечание. Я добавил недокументированный системный звук 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)
        }
    }
}