[Résolu] UITapGestureRecognizer : implémentation

iLandesiLandes Membre
juillet 2014 modifié dans API UIKit #1

Bonsoir à  tous,


 


Je cherche à  mettre en place la reconnaissance de geste dans mon ViewController (en l'occurrence le tap pour commencer).


 


Voici ce que j'ai écris 


 


- (void)viewDidLoad


{


    [super viewDidLoad];


    tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];


    [self.view addGestureRecognizer:tapRecognizer];


 

    // Do any additional setup after loading the view.


}


 

Dans le .h j'ai décalaré  UITapGestureRecognizer *tapRecognizer;


 


 



Dans l'état actuel la méthode handleTap: n'est jamais appelé. A cet instant elle ne contient qu'un NSLog.

 

J'ai lu pas mal de tuto et de code exemple car je souhaite détecter plusieurs type de gestes ; mais là  je galère.

 

D'avance merci de votre aide....

 

seb

 

Réponses

  • 1. Essaye d'utiliser les balises "code" lorsque tu mets du code, car même avec les couleurs, c'est pas très lisible...


     


    2. Je n'ai pas une grande expérience des Gesture, je n'ai utilisé qu'un longPress, mais voici ce que j'ai fait et qui fonctionne (pour le long press)



    if (self.longPressGestureRecognizer==nil) {
    UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPressGesture)];

    longPress.minimumPressDuration = 0.4;
    longPress.numberOfTouchesRequired = 1;
    longPress.delegate = self;

    self.longPressGestureRecognizer = longPress;
    [self.myMap addGestureRecognizer:self.longPressGestureRecognizer];

    }

    et la méthode handleLongPressure :



    if (self.longPressGestureRecognizer.state==UIGestureRecognizerStateEnded) {
    // Do something...
    }


    sans oublier bien sûr de mettre UIGestureRecognizerDelegate dans la liste des delegate...


  • AliGatorAliGator Membre, Modérateur
    T'aurais pas oublié de mettre le userInteractionsEnabled à  YES sur ta vue par hasard pour que le Touch ne soit pas ignoré ?
  • Bonjour,


     


    Merci pour votre aide. La nuit aussi porte conseil.


     


    Voici donc ce que j'ai fait et qui semble fonctionné : J'ai déplacé mon code dans l'init à  la place du viewdidload. @Ali je pense que userInteractionsEnabled est à  YES par défaut. J'avais rajouté puis j'ai supprimer cette ligne.


     


    Mon code pour exemple :



    - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
    {
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
    // etc...

    // View
    myView =[[MainView alloc] init];
    [self setView:myView];
    // etc...


    // Gesture

    // Tap Gesture
    UITapGestureRecognizer* tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
    [myView addGestureRecognizer:tapRecognizer];

    // Long Press Gesture
    UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPressGesture:)];

    longPress.minimumPressDuration = 0.4;
    longPress.numberOfTouchesRequired = 1;
    longPress.delegate = self;
    [myView addGestureRecognizer:longPress];

    }
    return self;
    }

    - (void)viewDidLoad
    {
    [super viewDidLoad];
    }

    - (void)didReceiveMemoryWarning
    {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
    }


    #pragma mark - Gesture with UIResponder

    - (void)handleTap:(UITapGestureRecognizer *)gestureReconizer {
    if ([gestureReconizer state] == UIGestureRecognizerStateEnded)
    {
    // handling code
    NSLog(@tap ended);
    }
    }

    - (void)handleLongPressGesture :(UITapGestureRecognizer *)gestureReconizer{
    if ([gestureReconizer state] == UIGestureRecognizerStateEnded)
    {
    NSLog(@long tap ended);
    }
    }


    Merci pour votre aide


    :p


  • Hello,


     


    Y a quand même un problème avec ton code, pourquoi tu crée une nouvelle vue ? 



    myView =[[MainView alloc] init];
    [self setView:myView];

    Normalement tu dois avoir une vue dans ton xib, qui est la vue principale de ton contrôleur, non ?


     


    Le premier code que tu as posté est le plus propre et c'est lui qui doit marcher :). Y a un problème quelque part avec ta vue principale du xib. vérifier les userActionEnabled,... ou bien dans le pire des cas, tu supprime la vue principale et tu remet une autre. 

  • En regardant de plus près, dans ton premier code, il manquait le delegate...


  • En regardant de plus près, dans ton premier code, il manquait le delegate...




    Le delegate est optionnel, ça vient d'ailleurs. 

  • Joanna CarterJoanna Carter Membre, Modérateur
    juillet 2014 modifié #8

    Voilà  ta problème !


     


    Tu as essayé de acceder les composantes avant qu'ils soient déserialisés. Ne touches pas les composantes que dans awakeFromNib ou plus tard - moi, je mets ce qui concerne les composantes dans viewDidLoad.


     


    Autre chose - pourquoi tu crées le view ? Si on chargeait le viewController d'un xib, le view est déserialisé au même temps, mais le view ne sera pas accessible avant le fin du déserialisation, soit dans viewDidLoad.


     


    Il faut déplacer le code comme ci-suivant :



    - (void)viewDidLoad
    {
    [super viewDidLoad];

    UITapGestureRecognizer* tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
    [self.view addGestureRecognizer:tapRecognizer];

    // Long Press Gesture
    UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPressGesture:)];

    longPress.minimumPressDuration = 0.4;
    longPress.numberOfTouchesRequired = 1;
    longPress.delegate = self;
    [self.view addGestureRecognizer:longPress];
    }

    En plus, il ne faut pas 'override' la méthode initWithNibName:bundle:


     


    Si ce code ne marche pas, c'est comment que tu crées le viewController ?


  • Joanna CarterJoanna Carter Membre, Modérateur

    Peut-être plus facile - mettre en place et connecter le gesture recognizer au viewController dans le XIB


  • Merci à  tous de vos réponses


     


    Un point que je n'avais pas précisé : je n'utilise pas de fichier xib. La vue est entièrement construite de manière programmatique (pas de storyboard dans mon projet).


     


    Je n'utilise pas le delegate dans cette exemple mais je vais devoir l'implémenter pour reconnaitre des gestes qui ont des débuts communs (swipe/pan).


     


    Quand à  vos remarques sur le fait d'overwriter le initWithNibName, je ne comprends pas bien ce que cela change. 


     


    En tout état de cause le code que j'ai publié fonctionne. Je posterais les améliorations en suivant.


     


    Un grand merci à  tous.


     


    seb


  • samirsamir Membre
    juillet 2014 modifié #11


    Un point que je n'avais pas précisé : je n'utilise pas de fichier xib. La vue est entièrement construite de manière programmatique (pas de storyboard dans mon projet).




     


    Donner des noms adéquat aux méthodes/classes/... est très important. Comme son nom l'indique le initWithNibName:.... est un init de la classe UIViewController avec un xib, le nibName comme paramètre. Si tu mets ce code, le prochain qui va lire ton code vas comprendre que tu utilises un xib pour ta vue alors que non. Y en a même qui disent que il ne faut jamais appeler cet init en dehors de sa classe, mais ça c'est une autre histoire.


     


     


    Si tu veux créer ta vue avec du code, il faut surcharger la méthode - (void)loadView de UIViewController. 


    et mettre la logique de gesture dans le viewDidLoad.


     


    https://developer.apple.com/library/ios/documentation/uikit/reference/UIViewController_Class/Reference/Reference.html#//apple_ref/occ/instm/UIViewController/loadView


  • @iLandes : vu que ta classe ne contient pas de xib, je pense que c'est pour cela que ça ne marchait pas : la méthode - viewDidLoad n'était peut-être jamais appelée.


  • iLandesiLandes Membre
    juillet 2014 modifié #13

    Je veins de finir la gestion des gestes de ma classe.


     


    Pour ceux que cela peut aider


     


    Voici un extrait du .h



    #import <UIKit/UIKit.h>
    #import "MainView.h"

    @interface MainViewController : UIViewController <UIGestureRecognizerDelegate>
    {
    UIPanGestureRecognizer* panGestureRecognizer;
    }

    @end

    Voici un extrait du .m



    #import "MainViewController.h"

    @interface MainViewController ()

    @end

    @implementation MainViewController

    #pragma mark - Basic methods

    - (instancetype)init
    {
    self = [self initWithNibName:nil bundle:nil];
    if (self) {

    // View
    myView =[[MainView alloc] init];
    [myView setBackgroundColor:[UIColor grayColor]];
    [self setView:myView];
    [self setupConstraints];

    // ....

    // Double Tap Gesture
    UITapGestureRecognizer* doubleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
    action:@selector(handleDoubleTap:)];
    [doubleTapRecognizer setNumberOfTapsRequired:2];
    [myView addGestureRecognizer:doubleTapRecognizer];


    // Swipe Gesture
    panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self
    action:@selector(handlePanGesture:)];

    [panGestureRecognizer setDelegate:self];
    [panGestureRecognizer setCancelsTouchesInView:NO];
    [myView addGestureRecognizer:panGestureRecognizer];


    // Long Press Gesture
    UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPressGesture:)];
    [myView addGestureRecognizer:longPress];
    }

    return self;
    }

    - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {

    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
    // XIB Initialisation

    }
    return self;
    }

    - (void)viewDidLoad
    {
    [super viewDidLoad];
    [self becomeFirstResponder];


    }

    - (void)didReceiveMemoryWarning
    {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
    }

    #pragma mark - Gesture with UIResponder

    // Delegate

    - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {

    if (gestureRecognizer == panGestureReconizer) {
    return YES;
    }
    else {
    return NO;
    }
    }

    // Selectors

    - (void)handleDoubleTap:(UITapGestureRecognizer *)gestureReconizer {
    if ([gestureReconizer state] == UIGestureRecognizerStateEnded)
    {
    NSLog(@double tap);
    }
    }

    - (void)handleLongPressGesture:(UITapGestureRecognizer *)gestureReconizer{
    if ([gestureReconizer state] == UIGestureRecognizerStateEnded)
    {
    NSLog(@long);
    }
    }

    - (void)handlePanGesture:(UITapGestureRecognizer *)gestureRecognizer{
    if ([gestureRecognizer state] == UIGestureRecognizerStateEnded)
    {
    NSLog(@pan);
    }
    }

    @end


    Un grand merci à  tous les contributeurs de ce post


     


    seb 


  • Joanna CarterJoanna Carter Membre, Modérateur
    juillet 2014 modifié #14

    Quelques petites corrections...



    #import <UIKit/UIKit.h>

    @interface MainViewController : UIViewController <UIGestureRecognizerDelegate>

    // n'utilise plus les ivars, surtout pas dans l'interface

    @end


    #import "MainViewController.h"
    #import "MainView.h"


    @interface MainViewController () <UIGestureRecognizerDelegate>

    @property (strong, nonatomic) UIPanGestureRecognizer* panGestureRecognizer;

    @end


    @implementation MainViewController #pragma mark - Basic methods

    // pas nécessaire
    - (instancetype)init
    {
    ...
    }

    // pas nécessaire
    - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
    {
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self)
    {
    // XIB Initialisation
    }

    return self;
    }

    // indispensable
    - (UIView *)loadView
    {
    // ici il faut ne faire que le suivant
    self.view = [[MainView alloc] init];
    }

    // indispensable
    - (void)viewDidLoad
    {
    [super viewDidLoad];

    // faire ici tous ce qui est nécessaire pour préparer la vue, pas ailleurs !!!

    [self.view setBackgroundColor:[UIColor grayColor]];

    [self setupConstraints];

    // ....

    // Double Tap Gesture
    UITapGestureRecognizer* doubleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleTap:)];

    [doubleTapRecognizer setNumberOfTapsRequired:2];

    [self.view addGestureRecognizer:doubleTapRecognizer];

    // Swipe Gesture
    self.panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)];

    [self.panGestureRecognizer setDelegate:self];

    [self.panGestureRecognizer setCancelsTouchesInView:NO];

    [self.view addGestureRecognizer:self.panGestureRecognizer];

    // Long Press Gesture
    UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPressGesture:)];

    [self.view addGestureRecognizer:longPress];
    }

    #pragma mark - Gesture with UIResponder

    // Delegate

    - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
    {
    if (gestureRecognizer == panGestureReconizer)
    {
    return YES;
    }
    else
    {
    return NO;
    }
    }

    // Selectors

    - (void)handleDoubleTap:(UITapGestureRecognizer *)gestureReconizer
    {
    if ([gestureReconizer state] == UIGestureRecognizerStateEnded)
    {
    NSLog(@double tap);
    }
    }

    - (void)handleLongPressGesture:(UITapGestureRecognizer *)gestureReconizer
    {
    if ([gestureReconizer state] == UIGestureRecognizerStateEnded)
    {
    NSLog(@long);
    }
    }

    - (void)handlePanGesture:(UITapGestureRecognizer *)gestureRecognizer
    {
    if ([gestureRecognizer state] == UIGestureRecognizerStateEnded)
    {
    NSLog(@pan);
    }
    }

    @end

    C'est très important que tu ne fais pas ce que tu as proposé  8--)


  • Serait-ce absurde de faire



    - (id)init
    {
    self = [super init] ;
    if (self)
    {
    [self loadView] ;
    }

    return self ;
    }

    ?


  • Joanna CarterJoanna Carter Membre, Modérateur


    Serait-ce absurde de faire



    - (id)init
    {
    self = [super init] ;
    if (self)
    {
    [self loadView] ;
    }

    return self ;
    }

    ?




    Non seulement absurde, c'est contre les directions d'Apple  ::)

  • AliGatorAliGator Membre, Modérateur


    Serait-ce absurde de faire


    - (id)init
    {
    self = [super init] ;
    if (self)
    {
    [self loadView] ;
    }

    return self ;
    }

    ?
    Il ne fait jamais appeler loadView soi même, ça sera appelé automatiquement par le VC quand il voit qu'il a besoin de charger la vue.

    Si tu l'appelles dans ton init :


    - Non seulement ça va appeler une méthode qui est normalement appelée automatiquement par le VC et seulement dans un certain contexte alors que là  tu l'appelles hors de contexte (c'est un peu la même erreur que si tu appelais drawRect: toi même sur une UIView ce qu'il ne faut jamais faire car drawRect est appelé automatiquement et surtout à  un moment précis de la phase de refresh d'UI de la RunLoop, en ayant créé lui-même le contexte graphique auparavant, etc, donc à  ne jamais appeler soi-même)


    - Mais en plus et surtout ça va charger la vue bien trop tôt, dès l'init, alors que non seulement ce n'est pas nécessaire mais surtout ça ne respecte pas le pattern de lazy-loading mis en place par les VC et qui en font entre autres tout leur intérêt.


    Un VC ne charge sa vue tout seul que quand c'est nécessaire, pas avant.
Connectez-vous ou Inscrivez-vous pour répondre.