93 votes

UITapGestureRecognizer - faites-le fonctionner au touché vers le bas, pas au touché vers le haut?

Ce que j'utilise l'événement de tap pour est très sensible au temps, donc je me demande s'il est possible de faire en sorte que UITapGestureRecognizer soit activé lorsque l'utilisateur touche simplement vers le bas, plutôt que de leur demander de lever aussi?

1 votes

Si cela aide, UITouch a une méthode touchesStarted. Mais cela n'utilise pas de reconnaissants de gestes, comme vous l'avez demandé.

202voto

Rob Caraway Points 398

Utilisez un UILongPressGestureRecognizer et définissez sa minimumPressDuration à 0. Il agira comme un touch down pendant l'état UIGestureRecognizerStateBegan.

Pour Swift 4+

func setupTap() {
    let touchDown = UILongPressGestureRecognizer(target:self, action: #selector(didTouchDown))
    touchDown.minimumPressDuration = 0
    view.addGestureRecognizer(touchDown)
}

@objc func didTouchDown(gesture: UILongPressGestureRecognizer) {
    if gesture.state == .began {
        doSomething()
    }
}

Pour Objective-C

-(void)setupLongPress
{
   self.longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(didLongPress:)];
   self.longPress.minimumPressDuration = 0;
   [self.view addGestureRecognizer:self.longPress];
}

-(void)didLongPress:(UILongPressGestureRecognizer *)gesture
{
   if (gesture.state == UIGestureRecognizerStateBegan){
      [self doSomething];
   }
}

5 votes

Pour les personnes qui aiment l'interface builder (comme moi), minimumPressDuration peut également être définie dans IB (merci à Rob pour cette excellente solution)

0 votes

Y a-t-il un moyen de détecter le toucher ici?

1 votes

@CalZone Oui, vérifiez l'état des gestes UIGestureRecognizerStateEnded

143voto

iAn Points 3599

Créez votre sous-classe personnalisée TouchDownGestureRecognizer et implémentez le geste dans touchesBegan:

TouchDownGestureRecognizer.h

#import 

@interface TouchDownGestureRecognizer : UIGestureRecognizer

@end

TouchDownGestureRecognizer.m

#import "TouchDownGestureRecognizer.h"
#import 

@implementation TouchDownGestureRecognizer
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    if (self.state == UIGestureRecognizerStatePossible) {
        self.state = UIGestureRecognizerStateRecognized;
    }
}

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
    self.state = UIGestureRecognizerStateFailed;
}

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
    self.state = UIGestureRecognizerStateFailed;
}

@end

implémentation:

#import "TouchDownGestureRecognizer.h"
    TouchDownGestureRecognizer *touchDown = [[TouchDownGestureRecognizer alloc] initWithTarget:self action:@selector(handleTouchDown:)];
    [yourView addGestureRecognizer:touchDown];

-(void)handleTouchDown:(TouchDownGestureRecognizer *)touchDown{
    NSLog(@"Down");
}

Implémentation en Swift:

import UIKit
import UIKit.UIGestureRecognizerSubclass

class TouchDownGestureRecognizer: UIGestureRecognizer
{
    override func touchesBegan(touches: Set, withEvent event: UIEvent)
    {
        if self.state == .Possible
        {
            self.state = .Recognized
        }
    }

    override func touchesMoved(touches: Set, withEvent event: UIEvent)
    {
        self.state = .Failed
    }

    override func touchesEnded(touches: Set, withEvent event: UIEvent)
    {
        self.state = .Failed
    }
}

Voici la syntaxe Swift pour 2017 à coller:

import UIKit.UIGestureRecognizerSubclass

class SingleTouchDownGestureRecognizer: UIGestureRecognizer {
    override func touchesBegan(_ touches: Set, with event: UIEvent) {
        if self.state == .possible {
            self.state = .recognized
        }
    }
    override func touchesMoved(_ touches: Set, with event: UIEvent) {
        self.state = .failed
    }
    override func touchesEnded(_ touches: Set, with event: UIEvent) {
        self.state = .failed
    }
}

Notez que c'est un remplacement direct pour UITap. Donc dans du code comme...

func add(tap v:UIView, _ action:Selector) {
    let t = UITapGestureRecognizer(target: self, action: action)
    v.addGestureRecognizer(t)
}

vous pouvez en toute sécurité passer à....

func add(hairtriggerTap v:UIView, _ action:Selector) {
    let t = SingleTouchDownGestureRecognizer(target: self, action: action)
    v.addGestureRecognizer(t)
}

Les tests montrent qu'elle ne sera pas appelée plus d'une fois. Cela fonctionne comme un remplacement direct; vous pouvez simplement alterner entre les deux appels.

10 votes

Augmentation +1 pour l'importation de "UIGestureRecognizerSubclass.h". Super.

3 votes

Ne devrait-on pas appeler super dans les méthodes touchesBegin, touchesMoved, touchesEnded?

3 votes

@JasonSilberman tu oublies #import

26voto

Suragch Points 197

Swift (sans sous-classe)

Voici une version Swift similaire à la réponse Objective-C de Rob Caraway.

L'idée est d'utiliser un reconnaiseur de gestes de pression longue avec la propriété minimumPressDuration réglée sur zéro plutôt que d'utiliser un reconnaiseur de gestes de tapotement. Cela est dû au fait que le reconnaiseur de gestes de pression longue signale les événements de début de toucher tandis que le reconnaiseur de gestes de tapotement ne le fait pas.

import UIKit
class ViewController: UIViewController {

    @IBOutlet weak var myView: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // Ajouter le reconnaiseur de gestes de "pression longue"
        let tap = UILongPressGestureRecognizer(target: self, action: #selector(tapHandler))
        tap.minimumPressDuration = 0
        myView.addGestureRecognizer(tap)
    }

    // Appelé par le reconnaiseur de gestes
    @objc func tapHandler(gesture: UITapGestureRecognizer) {

        // Gérer séparément les événements de toucher et de relâchement
        if gesture.state == .began {
            // Faire quelque chose...
            print("appui en cours")
        } else if gesture.state == .ended { // facultatif pour capturer l'événement de relâchement
            // Faire autre chose...
            print("relâchement de l'appui")
        }
    }
}

1 votes

@richy, Cela m'a semblé un peu comme un hack, aussi, puisque le nom est long press gesture recognizer, mais cette méthode est beaucoup plus facile que sous-classer la vue et comme tu l'as dit, ça fonctionne très bien.

0 votes

Cette solution provoque un problème de défilement dans les sous-classes de UIScrollView

0 votes

Je pense que la meilleure approche est le Custom Gesture recognizer

1voto

morizotter Points 734

Ceci est une autre solution. Créez une sous-classe de UIControl. Vous pouvez l'utiliser comme une UIView même dans Storyboard car UIControl est une sous-classe de UIView.

class TouchHandlingView: UIControl {
}

Et ajoutez une cible :

@IBOutlet weak var mainView: TouchHandlingView!
...

mainView.addTarget(self, action: "startAction:", forControlEvents: .TouchDown)
...

Ensuite, l'action désignée sera appelée comme un UIButton :

func startAction(sender: AnyObject) {
    print("start")
}

1voto

Lance Samaria Points 1995

J'avais besoin que ma vue ait une sensibilité accrue afin de pouvoir répondre dès qu'elle est touchée. En utilisant à la fois la réponse de @LESANG et également la réponse de @RobCaraway, cela a fonctionné. Cependant, le problème que j'ai rencontré avec les deux réponses était que j'ai perdu la capacité de reconnaître les balayages. J'avais besoin que ma vue effectue une rotation lors d'un balayage, mais dès que mon doigt touchait la vue, seul le tap était reconnu. Le tapRecognizer était trop sensible et ne pouvait pas différencier un tap d'un balayage.

Voici ce que j'ai créé en m'inspirant de la réponse de @LESANG combinée avec cette réponse et cette réponse.

J'ai mis 6 commentaires pour chaque événement.

import UIKit.UIGestureRecognizerSubclass

class SingleTouchDownGestureRecognizer: UIGestureRecognizer {

    var wasSwiped = false

    override func touchesBegan(_ touches: Set, with event: UIEvent) {

        guard let view = self.view else { return }
        guard let touches = event.touches(for: view) else { return } // 1. comparer que l'événement dans touchesBegan a des touchers pour la vue qui est la même que la vue à laquelle votre reconnaissieur de gestes a été attribué

        if touches.first != nil {
            print("Le doigt a touché !") // 2. c'est lorsque le doigt de l'utilisateur touche d'abord la vue et se trouve à l'emplacementA
            wasSwiped = false // 3. il semble que je n'avais pas besoin de le définir sur false car la propriété était déjà sur false mais pour une raison quelconque, lorsque je ne l'ai pas ajouté, cela ne répondait pas correctement. En gros, définissez ceci sur false
        }
    }

    override func touchesMoved(_ touches: Set, with event: UIEvent) {

        guard let touch = touches.first else { return }

        let newLocation = touch.location(in: self.view)
        let previousLocation = touch.previousLocation(in: self.view)

        if (newLocation.x > previousLocation.x) || (newLocation.x < previousLocation.x) {
            print("Le doigt se déplace à droite ou à gauche") // 4. lorsque le doigt de l'utilisateur touche d'abord, il est à l'emplacementA. Si l'utilisateur déplace son doigt vers la gauche ou la droite, alors le doigt n'est plus à l'emplacementA. Cela signifie qu'il a bougé, ce qui signifie qu'un balayage s'est produit, donc définissez la propriété "wasSwiped" sur true

            wasSwiped = true // 5. définir la propriété sur true car l'utilisateur a déplacé son doigt
        }
    }

    override func touchesEnded(_ touches: Set, with event: UIEvent) {
        print("Le doigt ne touche plus.") // 6. l'utilisateur a levé son doigt de la vue. Si "wasSwiped" est true alors ".fail" mais s'il n'y a pas eu de balayage alors ".recognize"

        if wasSwiped {
            self.state = .failed
        } else {
            self.state = .recognized
        }
    }
}

Et pour l'utiliser de sorte que la vue qui l'utilise obtienne une réponse instantanée et des gestes de balayage à gauche et à droite :

let tapGesture = SingleTouchDownGestureRecognizer(target: self, action: #selector(viewWasTapped(_:)))
myView.addGestureRecognizer(tapGesture)

let rightGesture = UISwipeGestureRecognizer(target: self, action: #selector(respondToSwipeGesture(recognizer:)))
rightGesture.direction = .right
myView.addGestureRecognizer(rightGesture)

let leftGesture = UISwipeGestureRecognizer(target: self, action: #selector(respondToSwipeGesture(recognizer:)))
leftGesture.direction = .left
myView.addGestureRecognizer(leftGesture)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X