Długie naciśnięcie na UITableView

191

Chciałbym poradzić sobie z długim naciśnięciem przycisku a, UITableViewCellaby wydrukować „menu szybkiego dostępu”. Czy ktoś już to zrobił?

W szczególności gest rozpoznaje UITableView?

foOg
źródło

Odpowiedzi:

427

Najpierw dodaj rozpoznawanie gestów długiego naciśnięcia do widoku tabeli:

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

Następnie w module obsługi gestów:

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

Trzeba uważać na to, aby nie zakłócało normalnego stukania komórki przez użytkownika, a także pamiętać, że handleLongPressmoże strzelać wiele razy (będzie to spowodowane zmianami stanu rozpoznawania gestów).

CDspace
źródło
1
Niesamowite !!! Wielkie dzięki! Ale ostatnie małe pytanie: dlaczego metoda handleLongPress jest wywoływana, gdy dotyk się kończy?
foOg
111
Korekta: uruchamia się wiele razy, aby wskazać różne stany gestu (rozpoczęty, zmieniony, zakończony itp.). Tak więc w metodzie obsługi sprawdź właściwość stanu rozpoznawania gestów, aby uniknąć wykonywania akcji w każdym stanie gestu. Np if (gestureRecognizer.state == UIGestureRecognizerStateBegan) .... :
3
Nie zapominaj, rozpoznawanie gestów można teraz dodawać do elementów interfejsu bezpośrednio w Konstruktorze interfejsów i łączyć metodą IBAction, więc ta odpowiedź jest jeszcze łatwiejsza ;-) (pamiętaj tylko o dołączeniu rozpoznawania do UITableView, a nie UITableViewCell...)
10
Potwierdź także protokół UIGestureRecognizerDelegate w pliku class.h
Vaquita,
1
Jak uniemożliwić nawigację widoku tabeli do komórki (jeśli zaimplementowano opcję „didSelectRowAtIndexPath?”)
Marchy
46

Użyłem odpowiedzi Anny-Kareniny i działa ona prawie doskonale z bardzo poważnym błędem.

Jeśli używasz sekcji, długie naciśnięcie tytułu sekcji da zły wynik naciśnięcia pierwszego wiersza w tej sekcji, dodałem poniżej poprawioną wersję (w tym filtrowanie fałszywych wywołań na podstawie stanu gestu według Sugestia Anny-Kareniny).

- (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
źródło
Cześć @marmor: Chcę zapytać, czy można zidentyfikować tylko część widoku, którego dotknął użytkownik?
Manthan
Hej, możesz do tego użyć hitTest ( developer.apple.com/library/ios/documentation/uikit/reference/… ). Sprawdź tę odpowiedź, na przykład, jak używać: stackoverflow.com/a/2793253/819355
marmor
Podczas gdy zaakceptowana odpowiedź działa. Rejestruje wiele pożarów, a także rejestruje podczas przeciągania na ekranie. Ta odpowiedź nie. Prawidłowa kopia i wklej odpowiedź. Dziękuję Ci.
ChrisOSX,
29

Odpowiedź w Swift 5 (Kontynuacja odpowiedzi Ricky w Swift)

Dodaj UIGestureRecognizerDelegatedo ViewController

 override func viewDidLoad() {
    super.viewDidLoad()

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

I funkcja:

@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
źródło
20

Oto wyjaśnione instrukcje łączące odpowiedź Dawn Song i odpowiedź Marmora.

Przeciągnij długi przycisk Rozpoznawanie gestów i upuść go w komórce tabeli. Przeskoczy na dół listy po lewej stronie.

wprowadź opis zdjęcia tutaj

Następnie podłącz rozpoznawanie gestów w taki sam sposób jak przycisk. wprowadź opis zdjęcia tutaj

Dodaj kod z Marmor w module obsługi akcji

- (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
źródło
2
Najlepsza odpowiedź moim zdaniem
Asen Kasimov
8
Long Press Gesture Recognizer należy zastosować do komórki widoku tabeli, a nie komórki widoku tabeli. Upuszczenie go do komórki widoku tabeli spowoduje, że wiersz 0 będzie słuchał długiego naciśnięcia.
Alex
14

Wygląda na bardziej wydajne dodawanie modułu rozpoznającego bezpośrednio do komórki, jak pokazano tutaj:

Naciśnij i przytrzymaj, aby wyświetlić komórki TableView, a następnie i teraz

(przewiń do przykładu na dole)

JR
źródło
7
W jaki sposób przydzielenie nowego obiektu rozpoznającego gesty dla każdego wiersza może być bardziej wydajne niż jednego rozpoznającego gest dla całej tabeli?
user2393462435,
7
Pamiętaj, że istnieje tylko kilka komórek utworzonych, jeśli dequeue działa poprawnie.
Mrówki
11

Odpowiedź w Swift:

Dodaj delegata UIGestureRecognizerDelegatedo swojego UITableViewController.

W ramach 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)

}

I funkcja:

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
źródło
6

Stworzyłem małą kategorię w UITableView na podstawie doskonałej odpowiedzi Anny Kareniny.

W ten sposób będziesz mieć wygodną metodę delegowania, do której jesteś przyzwyczajony, gdy masz do czynienia ze zwykłymi widokami tabel. Sprawdź to:

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

Jeśli chcesz użyć tego w UITableViewController, prawdopodobnie musisz podklasę i dostosować się do nowego protokołu.

Działa mi świetnie, mam nadzieję, że pomaga innym!

de.
źródło
Niesamowite wykorzystanie wzorów delegacji i kategorii
valeCocoa
5

Odpowiedź Swift 3, przy użyciu nowoczesnej składni, uwzględnienia innych odpowiedzi i wyeliminowania niepotrzebnego kodu.

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
źródło
2

Wystarczy dodać UILongPressGestureRecognizer do podanej komórki prototypowej w serii ujęć, a następnie przeciągnąć gest do pliku .m viewController, aby utworzyć metodę akcji. Zrobiłem to, jak powiedziałem.

DawnSong
źródło
Czy możesz wyjaśnić coś więcej? Czy uczyniłeś prototypową komórkę właściwością w swoim VC?
Ethan Parker
-2

Użyj właściwości znacznika czasu UITouch w touchach Rozpocznij, aby uruchomić stoper lub zatrzymać go po dotknięciu

Thomas Joulin
źródło
Dziękuję za odpowiedź, ale jak mogę wykryć, który wiersz dotyczy dotyku?
foOg
Mogę się mylić, ale nic nie jest w stanie Ci pomóc. Będziesz musiał uzyskać indeksy aktualnie widocznych komórek za pomocą [tableView indexPathsForVisibleRows], a następnie, korzystając z niektórych obliczeń (przesunięcie tableView od góry + X razy rzędy), będziesz wiedział, że współrzędne twojego palca są na którym rząd.
Thomas Joulin,
Jestem pewien, że istnieje łatwiejszy sposób, aby to zrobić, w każdym razie, jeśli masz inny pomysł, będę tutaj :)
fo
Z przyjemnością dowiem się również, czy możliwe jest coś łatwiejszego. Ale nie sądzę, żeby tak było, głównie dlatego, że Apple nie chce, abyśmy obsługiwali interakcje ... Wygląda to na sposób myślenia w Androidzie w tym „menu szybkiego dostępu”. Gdyby to była moja aplikacja, poradzę sobie z nią jak na Twitterze. Przeciągnięcie w lewo wyświetla opcje
Thomas Joulin,
Tak, zastanowiłem się nad tym, więc jeśli naprawdę nie mogę tego zrobić w przypadku długiego wydarzenia prasowego, użyję metody machnięcia. Ale może ktoś w przepełnienie stosu to zrobił ...
foOg