Premere a lungo su UITableView


Risposte:


427

Per prima cosa aggiungi il riconoscimento dei gesti a pressione lunga alla vista tabella:

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

Quindi nel gestore dei gesti:

-(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);
    }
}

Devi stare attento con questo in modo che non interferisca con il normale tocco della cella dell'utente e anche notare che handleLongPresspotrebbe sparare più volte (questo sarà dovuto alle modifiche dello stato del riconoscimento dei gesti).


1
Eccezionale !!! Molte grazie! Ma un'ultima piccola domanda: perché il metodo handleLongPress è chiamata quando il tocco è finito ???
dal

111
Correzione: si attiva più volte per indicare i diversi stati del gesto (iniziato, modificato, terminato, ecc.). Quindi, nel metodo del gestore, controlla la proprietà dello stato del riconoscimento gesti per evitare di eseguire l'azione in ogni stato del gesto. Ad esempio: if (gestureRecognizer.state == UIGestureRecognizerStateBegan) ....

3
Non dimenticare, ora i riconoscitori di gesti possono essere aggiunti agli elementi dell'interfaccia utente direttamente in Interface Builder e collegati tramite un metodo IBAction, quindi questa risposta è ancora più semplice ;-) (ricorda solo di collegare il riconoscitore al UITableView, non al UITableViewCell...)

10
Conferma anche per il protocollo UIGestureRecognizerDelegate nel file di class.h
Vaquita

1
Come si impedisce alla vista tabella di spostarsi all'interno della cella (se è stato implementato 'didSelectRowAtIndexPath'?)
Marchy

46

Ho usato la risposta di Anna-Karenina e funziona quasi perfettamente con un bug molto serio.

Se stai usando sezioni, premendo a lungo il titolo della sezione ti darà un risultato sbagliato nel premere la prima riga su quella sezione, ho aggiunto una versione fissa di seguito (incluso il filtraggio delle chiamate fittizie in base allo stato del gesto, per Suggerimento di 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);
            }
        }
    }
}

Ciao @marmor: voglio chiedere che è possibile identificare solo una parte di una vista quale utente ha toccato?
Manthan,

Ehi, puoi usare hitTest per questo ( developer.apple.com/library/ios/documentation/uikit/reference/… ). Controlla questa risposta per esempio su come usare: stackoverflow.com/a/2793253/819355
marmor

Mentre la risposta accettata funziona. Registra più incendi, insieme alla registrazione mentre si trascina sullo schermo. Questa risposta no. Una risposta legittima per copiare e incollare. Grazie.
ChrisOSX,

29

Risposta in Swift 5 (Continuazione della risposta di Ricky in Swift)

Aggiungi il UIGestureRecognizerDelegateal tuo ViewController

 override func viewDidLoad() {
    super.viewDidLoad()

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

E la funzione:

@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)")
    }
}

20

Ecco le istruzioni chiarite che combinano la risposta di Dawn Song e la risposta di Marmor.

Trascina un lungo Premi Riconoscimento gesti e rilascialo nella cella della tabella. Salterà in fondo all'elenco a sinistra.

inserisci qui la descrizione dell'immagine

Quindi collegare il riconoscimento gesti nello stesso modo in cui si collegherebbe un pulsante. inserisci qui la descrizione dell'immagine

Aggiungi il codice da Marmor nel gestore dell'azione

- (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);
        }
    }
}

}


2
La migliore risposta secondo me
Asen Kasimov il

8
Il riconoscimento gesti con pressione prolungata deve essere applicato alla cella della vista tabella e non della vista tabella. Rilasciandolo nella cella della vista tabella si avrà solo la riga 0 che ascolta la pressione lunga.
Alex

14

Sembra essere più efficiente per aggiungere il riconoscitore direttamente alla cella come mostrato qui:

Tocca e tieni premuto per le celle TableView, quindi e ora

(scorrere fino all'esempio in basso)


7
In che modo l'allocazione di un nuovo oggetto riconoscimento gesti per ogni riga può essere più efficiente di un singolo riconoscimento per l'intera tabella?
user2393462435

7
Ricorda però che sono state create solo poche celle se il dequeue funziona correttamente.
Formiche

11

Risposta in Swift:

Aggiungi delegato UIGestureRecognizerDelegateal tuo UITableViewController.

All'interno di 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)

}

E la funzione:

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)")
    }

}

6

Ho messo insieme una piccola categoria su UITableView basata sull'eccellente risposta di Anna Karenina.

In questo modo avrai un comodo metodo delegato come quello a cui sei abituato quando hai a che fare con le normali visualizzazioni delle tabelle. Controlla:

//  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];
        }
    }
}

Se si desidera utilizzarlo in un UITableViewController, probabilmente è necessario eseguire la sottoclasse e conformarsi al nuovo protocollo.

Funziona benissimo per me, spero che aiuti gli altri!


Incredibile utilizzo di modelli di deleghe e categorie
valeCocoa

5

Risposta rapida 3, usando la sintassi moderna, incorporando altre risposte ed eliminando il codice non necessario.

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
}

2

Aggiungi UILongPressGestureRecognizer alla cella del prototipo specificata nello storyboard, quindi sposta il gesto sul file .m di viewController per creare un metodo di azione. L'ho fatto come ho detto.


Puoi spiegarci un po 'di più? Hai reso la cella prototipo una proprietà nel tuo VC?
Ethan Parker,

-2

Utilizzare la proprietà timestamp UITouch in touchesBegan per avviare un timer o interromperlo quando viene toccato Ended


Grazie per la risposta, ma come posso rilevare quale riga è interessata dal tocco?
dal

Potrei sbagliarmi, ma non viene fornito nulla per aiutarti. Dovrai ottenere gli indici delle celle visibili correnti con [tableView indexPathsForVisibleRows] e poi, usando alcuni calcoli (il tuo tableView offset dall'alto + X volte le righe) saprai che le coordinate del tuo dito sono su quale riga.
Thomas Joulin,

Sono sicuro che ci sia un modo più semplice per farlo, in ogni caso, se avete un'altra idea, io sarò qui :)
foog

Sarei felice di sapere anche se è possibile fare qualcosa di più semplice. Ma non credo che ci sia, soprattutto perché non è il modo in cui Apple vuole che gestiamo le interazioni ... Sembra un modo Android di pensare a questo "menu di accesso rapido". Se fosse la mia app, la gestirò come l'app di Twitter. Un colpo a sinistra mostra le opzioni
Thomas Joulin,

Sì, ci ho pensato, quindi se davvero non riesco a farlo con un evento a lunga stampa, userò il metodo di scorrimento. Ma, forse qualcuno in stack overflow di fatto ...
foog
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.