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?

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).


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

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

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

}


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)


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

}

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!


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
}

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.


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


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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.