Drücken Sie lange auf UITableView

191

Ich möchte ein langes Drücken auf a UITableViewCellausführen, um ein "Schnellzugriffsmenü" zu drucken. Hat das schon jemand gemacht?

Besonders die Geste erkennen an UITableView?

foOg
quelle

Antworten:

427

Fügen Sie zuerst den Gestenerkenner für langes Drücken zur Tabellenansicht hinzu:

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

Dann im Gestenhandler:

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

Sie müssen damit vorsichtig sein, damit das normale Tippen des Benutzers auf die Zelle nicht beeinträchtigt wird. Beachten Sie auch, dass dies handleLongPressmöglicherweise mehrmals ausgelöst wird (dies ist auf Änderungen des Status der Gestenerkennung zurückzuführen).

CDspace
quelle
1
Genial !!! Vielen Dank! Aber eine letzte kleine Frage: Warum wird die Methode handleLongPress aufgerufen, wenn die Berührung beendet ist?
Für den
111
Korrektur: Es wird mehrmals ausgelöst, um die verschiedenen Zustände der Geste anzuzeigen (begonnen, geändert, beendet usw.). Überprüfen Sie daher bei der Handler-Methode die Statuseigenschaft des Gestenerkenners, um zu vermeiden, dass die Aktion bei jedem Status der Geste ausgeführt wird. ZB : if (gestureRecognizer.state == UIGestureRecognizerStateBegan) ....
3
Vergessen Sie nicht, dass Gestenerkenner jetzt direkt im Interface Builder zu UI-Elementen hinzugefügt und über eine IBAction-Methode verbunden werden können, sodass diese Antwort noch einfacher ist ;-) (Denken Sie daran, den Erkenner an den UITableView, nicht an den UITableViewCell... anzuhängen. )
10
Bestätigen Sie auch das UIGestureRecognizerDelegate-Protokoll in der Datei class.h
Vaquita
1
Wie verhindern Sie die Tabellenansicht in die Zelle aus der Navigation (wenn Sie ‚DidSelectRowAtIndexPath‘ umgesetzt haben?)
Marchy
46

Ich habe die Antwort von Anna-Karenina verwendet und sie funktioniert fast hervorragend mit einem sehr schwerwiegenden Fehler.

Wenn Sie Abschnitte verwenden und lange auf den Abschnittstitel drücken, erhalten Sie ein falsches Ergebnis beim Drücken der ersten Zeile in diesem Abschnitt. Ich habe unten eine feste Version hinzugefügt (einschließlich der Filterung von Dummy-Anrufen basierend auf dem Gestenstatus pro Anna-Karenina Vorschlag).

- (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
quelle
Hallo @marmor: Ich möchte fragen, ob es möglich ist, nur einen Teil einer Ansicht zu identifizieren, den der Benutzer berührt hat.
Manthan
Hey, dafür kannst du hitTest verwenden ( developer.apple.com/library/ios/documentation/uikit/reference/… ). Überprüfen Sie diese Antwort zum Beispiel zur Verwendung: stackoverflow.com/a/2793253/819355
marmor
Während die akzeptierte Antwort funktioniert. Es protokolliert mehrere Brände sowie die Protokollierung beim Ziehen auf dem Bildschirm. Diese Antwort nicht. Eine legitime Antwort zum Kopieren und Einfügen. Danke.
ChrisOSX
29

Antwort in Swift 5 (Fortsetzung von Rickys Antwort in Swift)

Fügen Sie das UIGestureRecognizerDelegateIhrem ViewController hinzu

 override func viewDidLoad() {
    super.viewDidLoad()

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

Und die Funktion:

@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
quelle
20

Hier finden Sie geklärte Anweisungen, die Dawn Songs Antwort und Marmors Antwort kombinieren.

Ziehen Sie einen langen Press Gestenerkenner und legen Sie ihn in Ihrer Tabellenzelle ab. Es springt zum Ende der Liste auf der linken Seite.

Geben Sie hier die Bildbeschreibung ein

Schließen Sie dann den Gestenerkenner genauso an, wie Sie eine Schaltfläche anschließen würden. Geben Sie hier die Bildbeschreibung ein

Fügen Sie den Code von Marmor im Aktionshandler hinzu

- (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
quelle
2
Die beste Antwort meiner Meinung nach
Asen Kasimov
8
Long Press Gesture Recognizer sollte auf die Tabellenansicht und nicht auf die Tabellenansichtszelle angewendet werden. Wenn Sie es in die Zelle der Tabellenansicht legen, hört Zeile 0 nur langes Drücken.
Alex
14

Es scheint effizienter zu sein, den Erkenner wie hier gezeigt direkt zur Zelle hinzuzufügen:

Tippen und halten Sie für TableView-Zellen, damals und heute

(Scrollen Sie zum Beispiel unten)

JR
quelle
7
Wie kann das Zuweisen eines neuen Gestenerkennungsobjekts für jede Zeile effizienter sein als ein einzelner Erkenner für die gesamte Tabelle?
user2393462435
7
Denken Sie daran, dass nur wenige Zellen erstellt werden, wenn die Warteschlange ordnungsgemäß funktioniert.
Ameisen
11

Antwort in Swift:

Fügen Sie UIGestureRecognizerDelegateIhrem UITableViewController einen Delegaten hinzu.

Innerhalb von 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)

}

Und die Funktion:

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
quelle
6

Ich habe eine kleine Kategorie auf UITableView zusammengestellt, basierend auf Anna Kareninas hervorragender Antwort.

Auf diese Weise haben Sie eine bequeme Delegierungsmethode, wie Sie es gewohnt sind, wenn Sie mit normalen Tabellenansichten arbeiten. Hör zu:

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

Wenn Sie dies in einem UITableViewController verwenden möchten, müssen Sie wahrscheinlich eine Unterklasse erstellen und das neue Protokoll einhalten.

Es funktioniert großartig für mich, hoffe es hilft anderen!

de.
quelle
Erstaunliche Verwendung von Delegations- und Kategoriemustern
valeCocoa
5

Swift 3-Antwort mit moderner Syntax, Einbeziehung anderer Antworten und Eliminierung nicht benötigten Codes.

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
quelle
2

Fügen Sie einfach UILongPressGestureRecognizer zu der angegebenen Prototypzelle im Storyboard hinzu und ziehen Sie die Geste in die .m-Datei von viewController, um eine Aktionsmethode zu erstellen. Ich habe es geschafft, wie ich sagte.

DawnSong
quelle
Kannst du etwas mehr erklären? Haben Sie die Prototypzelle zu einer Eigenschaft in Ihrem VC gemacht?
Ethan Parker
-2

Verwenden Sie die UITouch-Zeitstempeleigenschaft in touchBegan, um einen Timer zu starten oder zu stoppen, wenn touchEnded ausgelöst wurde

Thomas Joulin
quelle
Vielen Dank für Ihre Antwort, aber wie kann ich feststellen, welche Zeile von der Berührung betroffen ist?
Für den
Ich könnte mich irren, aber nichts wird bereitgestellt, um Ihnen dabei zu helfen. Sie müssen die Indizes der aktuell sichtbaren Zellen mit [tableView indexPathsForVisibleRows] abrufen und dann mithilfe einiger Berechnungen (Ihr tableView-Versatz von oben + X mal den Zeilen) wissen, auf welchen Koordinaten sich Ihr Finger befindet Reihe.
Thomas Joulin
Ich bin mir sicher, dass es einen einfacheren Weg gibt, wenn Sie eine andere Idee haben, werde ich hier sein :)
für den
Ich würde mich auch freuen zu wissen, ob etwas Einfacheres möglich ist. Aber ich glaube nicht, dass dies der Fall ist, vor allem, weil Apple nicht möchte, dass wir mit Interaktionen umgehen ... Es sieht aus wie eine Android-Denkweise für dieses "Schnellzugriffsmenü". Wenn es meine App wäre, würde ich damit umgehen wie mit der Twitter-App. Ein Wischen nach links zeigt die Optionen
Thomas Joulin
Ja, ich habe darüber nachgedacht. Wenn ich es mit einem langen Presseereignis wirklich nicht schaffen kann, verwende ich die Swipe-Methode. Aber vielleicht hat es jemand im Stapelüberlauf getan ...
für den