Appui long sur UITableView

191

Je voudrais gérer un appui long sur a UITableViewCellpour imprimer un "menu d'accès rapide". Est-ce que quelqu'un a déjà fait ça?

Particulièrement le geste reconnaît-il UITableView?

foOg
la source

Réponses:

428

Ajoutez d'abord le module de reconnaissance des gestes de pression longue à la vue de tableau:

UILongPressGestureRecognizer *lpgr = [[UILongPressGestureRecognizer alloc] 
  initWithTarget:self action:@selector(handleLongPress:)];
lpgr.minimumPressDuration = 2.0; //seconds
lpgr.delegate = self;
[self.myTableView addGestureRecognizer:lpgr];
[lpgr release];

Puis dans le gestionnaire de gestes:

-(void)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer
{
    CGPoint p = [gestureRecognizer locationInView:self.myTableView];

    NSIndexPath *indexPath = [self.myTableView indexPathForRowAtPoint:p];
    if (indexPath == nil) {
        NSLog(@"long press on table view but not on a row");
    } else if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {
        NSLog(@"long press on table view at row %ld", indexPath.row);
    } else {
        NSLog(@"gestureRecognizer.state = %ld", gestureRecognizer.state);
    }
}

Vous devez faire attention à cela afin que cela n'interfère pas avec le tapotement normal de l'utilisateur sur la cellule et notez également que cela handleLongPresspeut se déclencher plusieurs fois (cela sera dû aux changements d'état de la reconnaissance de mouvement).

CDspace
la source
1
Impressionnant !!! Merci beaucoup! Mais une dernière petite question: pourquoi la méthode handleLongPress est-elle appelée à la fin du toucher ???
FOOG
111
Correction: il se déclenche plusieurs fois pour indiquer les différents états du geste (commencé, modifié, terminé, etc.). Donc, dans la méthode du gestionnaire, vérifiez la propriété state du module de reconnaissance de mouvement pour éviter de faire l'action à chaque état du mouvement. Par exemple: if (gestureRecognizer.state == UIGestureRecognizerStateBegan) ....
3
N'oubliez pas que les outils de reconnaissance de gestes peuvent désormais être ajoutés aux éléments de l'interface utilisateur directement dans Interface Builder et connectés via une méthode IBAction, donc cette réponse est encore plus simple ;-) (n'oubliez pas de joindre le module de reconnaissance au UITableView, pas au UITableViewCell...)
10
Confirmez également au protocole UIGestureRecognizerDelegate dans le fichier class.h
Vaquita
1
Comment empêcher la vue tableau de naviguer dans la cellule (si vous avez implémenté 'didSelectRowAtIndexPath'?)
Marchy
46

J'ai utilisé la réponse d'Anna-Karenina, et cela fonctionne presque très bien avec un bug très sérieux.

Si vous utilisez des sections, appuyer longuement sur le titre de la section vous donnera un mauvais résultat en appuyant sur la première ligne de cette section, j'ai ajouté une version fixe ci-dessous (y compris le filtrage des appels factices en fonction de l'état du geste, par Suggestion d'Anna-Karenina).

- (IBAction)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer
{
    if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {

        CGPoint p = [gestureRecognizer locationInView:self.tableView];

        NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:p];
        if (indexPath == nil) {
            NSLog(@"long press on table view but not on a row");
        } else {
            UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
            if (cell.isHighlighted) {
                NSLog(@"long press on table view at section %d row %d", indexPath.section, indexPath.row);
            }
        }
    }
}
marmor
la source
Salut @marmor: Je veux demander qu'il est possible d'identifier uniquement une partie d'une vue que l'utilisateur a touchée?
Manthan
Hé, vous pouvez utiliser hitTest pour cela ( developer.apple.com/library/ios/documentation/uikit/reference/… ). Vérifiez cette réponse par exemple sur la façon d'utiliser: stackoverflow.com/a/2793253/819355
marmor
Alors que la réponse acceptée fonctionne. Il enregistre plusieurs incendies, ainsi que la journalisation tout en faisant glisser sur l'écran. Cette réponse ne l'est pas. Une réponse légitime de copier-coller. Merci.
ChrisOSX
30

Réponse dans Swift 5 (Suite de la réponse de Ricky dans Swift)

Ajoutez le UIGestureRecognizerDelegateà votre ViewController

 override func viewDidLoad() {
    super.viewDidLoad()

    //Long Press
    let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress))
    longPressGesture.minimumPressDuration = 0.5
    self.tableView.addGestureRecognizer(longPressGesture)
 }

Et la fonction:

@objc func handleLongPress(longPressGesture: UILongPressGestureRecognizer) {
    let p = longPressGesture.location(in: self.tableView)
    let indexPath = self.tableView.indexPathForRow(at: p)
    if indexPath == nil {
        print("Long press on table view, not row.")
    } else if longPressGesture.state == UIGestureRecognizer.State.began {
        print("Long press on row, at \(indexPath!.row)")
    }
}
Ben
la source
20

Voici des instructions clarifiées combinant la réponse de Dawn Song et la réponse de Marmor.

Faites glisser un outil de reconnaissance de gestes et déposez-le dans votre cellule de tableau. Il sautera au bas de la liste sur la gauche.

entrez la description de l'image ici

Connectez ensuite le module de reconnaissance de gestes de la même manière que vous connectez un bouton. entrez la description de l'image ici

Ajoutez le code de Marmor dans le gestionnaire d'action

- (IBAction)handleLongPress:(UILongPressGestureRecognizer *)sender {
if (sender.state == UIGestureRecognizerStateBegan) {

    CGPoint p = [sender locationInView:self.tableView];

    NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:p];
    if (indexPath == nil) {
        NSLog(@"long press on table view but not on a row");
    } else {
        UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
        if (cell.isHighlighted) {
            NSLog(@"long press on table view at section %d row %d", indexPath.section, indexPath.row);
        }
    }
}

}

Ryan Heitner
la source
2
La meilleure réponse à mon avis
Asen Kasimov
8
La reconnaissance de mouvement de pression longue doit être appliquée à la vue de tableau et non à la cellule de vue de tableau. Si vous le déposez dans la cellule de vue du tableau, la ligne 0 ne sera écoutée que pendant un appui long.
Alex
14

Il semble plus efficace d'ajouter le module de reconnaissance directement à la cellule, comme indiqué ici:

Appuyez et maintenez pour les cellules TableView, hier et maintenant

(faites défiler jusqu'à l'exemple en bas)

JR
la source
7
Comment l'allocation d'un nouvel objet de reconnaissance de mouvement pour chaque ligne peut-elle être plus efficace qu'une seule reconnaissance pour l'ensemble de la table?
user2393462435
7
N'oubliez pas qu'il n'y a que quelques cellules créées si le retrait de la file d'attente fonctionne correctement.
Ants
11

Réponse dans Swift:

Ajoutez un délégué UIGestureRecognizerDelegateà votre UITableViewController.

Dans UITableViewController:

override func viewDidLoad() {
    super.viewDidLoad()

    let longPressGesture:UILongPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: "handleLongPress:")
    longPressGesture.minimumPressDuration = 1.0 // 1 second press
    longPressGesture.delegate = self
    self.tableView.addGestureRecognizer(longPressGesture)

}

Et la fonction:

func handleLongPress(longPressGesture:UILongPressGestureRecognizer) {

    let p = longPressGesture.locationInView(self.tableView)
    let indexPath = self.tableView.indexPathForRowAtPoint(p)

    if indexPath == nil {
        print("Long press on table view, not row.")
    }
    else if (longPressGesture.state == UIGestureRecognizerState.Began) {
        print("Long press on row, at \(indexPath!.row)")
    }

}
Ricky
la source
6

J'ai rassemblé une petite catégorie sur UITableView basée sur l'excellente réponse d'Anna Karenina.

Ainsi, vous aurez une méthode de délégué pratique, comme vous en avez l'habitude lorsque vous traitez avec des vues de table normales. Vérifiez-le:

//  UITableView+LongPress.h

#import <UIKit/UIKit.h>

@protocol UITableViewDelegateLongPress;

@interface UITableView (LongPress) <UIGestureRecognizerDelegate>
@property(nonatomic,assign)   id <UITableViewDelegateLongPress>   delegate;
- (void)addLongPressRecognizer;
@end


@protocol UITableViewDelegateLongPress <UITableViewDelegate>
- (void)tableView:(UITableView *)tableView didRecognizeLongPressOnRowAtIndexPath:(NSIndexPath *)indexPath;
@end



//  UITableView+LongPress.m

#import "UITableView+LongPress.h"

@implementation UITableView (LongPress)
@dynamic delegate;

- (void)addLongPressRecognizer {
    UILongPressGestureRecognizer *lpgr = [[UILongPressGestureRecognizer alloc]
                                          initWithTarget:self action:@selector(handleLongPress:)];
    lpgr.minimumPressDuration = 1.2; //seconds
    lpgr.delegate = self;
    [self addGestureRecognizer:lpgr];
}


- (void)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer
{
    CGPoint p = [gestureRecognizer locationInView:self];

    NSIndexPath *indexPath = [self indexPathForRowAtPoint:p];
    if (indexPath == nil) {
        NSLog(@"long press on table view but not on a row");
    }
    else {
        if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {
            // I am not sure why I need to cast here. But it seems to be alright.
            [(id<UITableViewDelegateLongPress>)self.delegate tableView:self didRecognizeLongPressOnRowAtIndexPath:indexPath];
        }
    }
}

Si vous souhaitez l'utiliser dans un UITableViewController, vous devez probablement sous-classer et vous conformer au nouveau protocole.

Cela fonctionne très bien pour moi, j'espère que cela aidera les autres!

de.
la source
Utilisation incroyable des modèles de délégation et de catégorie
valeCocoa
5

Réponse Swift 3, utilisant une syntaxe moderne, incorporant d'autres réponses et éliminant le code inutile.

override func viewDidLoad() {
    super.viewDidLoad()
    let recognizer = UILongPressGestureRecognizer(target: self, action: #selector(tablePressed))
    tableView.addGestureRecognizer(recognizer)
 }

@IBAction func tablePressed(_ recognizer: UILongPressGestureRecognizer) {
    let point = recognizer.location(in: tableView)

    guard recognizer.state == .began,
          let indexPath = tableView.indexPathForRow(at: point),
          let cell = tableView.cellForRow(at: indexPath),
          cell.isHighlighted
    else {
        return
    }

    // TODO
}
Phatmann
la source
2

Ajoutez simplement UILongPressGestureRecognizer à la cellule prototype donnée dans le storyboard, puis tirez le geste vers le fichier .m de viewController pour créer une méthode d'action. Je l'ai fait comme je l'ai dit.

DawnSong
la source
Pouvez-vous expliquer un peu plus? Avez-vous fait de la cellule prototype une propriété de votre VC?
Ethan Parker
-2

Utilisez la propriété d'horodatage UITouch dans touchesBegan pour lancer un minuteur ou l'arrêter lorsque touchEnded a été déclenché

Thomas Joulin
la source
Merci pour votre réponse mais comment puis-je détecter quelle ligne est concernée par le toucher?
FOOG
Je me trompe peut-être, mais rien n'est prévu pour vous aider à le faire. Vous devrez obtenir les index des cellules visibles actuelles avec [tableView indexPathsForVisibleRows] puis, en utilisant certains calculs (votre tableView offset à partir du haut + X fois les lignes), vous saurez que les coordonnées de votre doigt sont sur lesquelles rangée.
Thomas Joulin
Je suis sûr qu'il ya un moyen plus facile de le faire, de toute façon si vous avez une autre idée, je serai ici :)
FOOG
Je serais heureux de savoir aussi si quelque chose de plus simple est possible. Mais je ne pense pas que ce soit le cas, principalement parce qu'Apple ne veut pas que nous gérions les interactions ... Cela ressemble à une façon Android de penser ce "menu d'accès rapide". Si c'était mon application, je la gérerai comme l'application Twitter. Un balayage vers la gauche affiche les options
Thomas Joulin
Oui, j'y ai pensé, donc si je ne peux vraiment pas le faire avec un événement de presse longue, j'utiliserai la méthode de balayage. Mais, peut - être quelqu'un dans la pile débordement a fait ...
FOOG