Pulsación larga en UITableView

Respuestas:

427

Primero agregue el reconocedor de gestos de pulsación larga a la vista de tabla:

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

Luego, en el controlador de gestos:

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

Debe tener cuidado con esto para que no interfiera con el toque normal de la celda del usuario y también tenga en cuenta que handleLongPresspuede dispararse varias veces (esto se debe a los cambios de estado del reconocedor de gestos).

CDspace
fuente
1
Increíble !!! ¡Muchas gracias! Pero una última pequeña pregunta: ¿Por qué el método handleLongPress es llamar cuando terminó el toque?
foOg
111
Corrección: se dispara varias veces para indicar los diferentes estados del gesto (comenzó, cambió, finalizó, etc.). Entonces, en el método del controlador, verifique la propiedad de estado del reconocedor de gestos para evitar realizar la acción en cada estado del gesto. Por ejemplo: if (gestureRecognizer.state == UIGestureRecognizerStateBegan) ....
3
No olvide que los reconocedores de gestos ahora se pueden agregar a los elementos de la interfaz de usuario directamente en Interface Builder y conectarse a través de un método IBAction, por lo que esta respuesta es aún más fácil ;-) (solo recuerde adjuntar el reconocedor al UITableView, no al UITableViewCell...)
10
Confirme también con el protocolo UIGestureRecognizerDelegate en el archivo class.h
Vaquita
1
¿Cómo evita que la vista de tabla navegue hacia la celda (si ha implementado 'didSelectRowAtIndexPath')?
Marzo
46

He usado la respuesta de Anna-Karenina, y funciona casi genial con un error muy grave.

Si está utilizando secciones, presionar prolongadamente el título de la sección le dará un resultado incorrecto al presionar la primera fila de esa sección, agregué una versión fija a continuación (que incluye el filtrado de llamadas ficticias basadas en el estado del gesto, por Sugerencia de 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
fuente
Hola @marmor: ¿Quiero preguntar si es posible identificar solo una parte de una vista que el usuario tocó?
Manthan
Oye, puedes usar hitTest para eso ( developer.apple.com/library/ios/documentation/uikit/reference/… ). Verifique esta respuesta, por ejemplo, sobre cómo usar: stackoverflow.com/a/2793253/819355
marmor
Si bien la respuesta aceptada funciona. Registra varios incendios, junto con el registro mientras arrastra en la pantalla. Esta respuesta no. Una respuesta legítima de copiar y pegar. Gracias.
ChrisOSX
29

Respuesta en Swift 5 (Continuación de la respuesta de Ricky en Swift)

Agregue el UIGestureRecognizerDelegatea su ViewController

 override func viewDidLoad() {
    super.viewDidLoad()

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

Y la función:

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

Aquí hay instrucciones aclaradas que combinan la respuesta de Dawn Song y la respuesta de Marmor.

Arrastre un largo reconocimiento de gestos y suéltelo en la celda de la tabla. Saltará al final de la lista a la izquierda.

ingrese la descripción de la imagen aquí

Luego conecte el reconocedor de gestos de la misma manera que conectaría un botón. ingrese la descripción de la imagen aquí

Agregue el código de Marmor en el controlador de acción

- (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
fuente
2
La mejor respuesta en mi opinión
Asen Kasimov
8
El reconocimiento de gestos de pulsación larga debe aplicarse a la vista de tabla y no a la celda de vista de tabla. Al soltarlo en la celda de vista de tabla solo la fila 0 escuchará la pulsación larga.
Alex
14

Parece ser más eficiente agregar el reconocedor directamente a la celda como se muestra aquí:

Toque y sostenga para las celdas TableView, entonces y ahora

(desplácese al ejemplo en la parte inferior)

JR
fuente
77
¿Cómo puede ser más eficiente asignar un nuevo objeto de reconocimiento de gestos para cada fila que un único reconocedor para toda la tabla?
user2393462435
77
Sin embargo, recuerde que solo se crean unas pocas celdas si la eliminación de cola funciona correctamente.
Hormigas
11

Respuesta en Swift:

Agregue delegado UIGestureRecognizerDelegatea su UITableViewController.

Dentro de 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)

}

Y la función:

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

Reuní una pequeña categoría en UITableView basada en la excelente respuesta de Anna Karenina.

De esta manera, tendrá un método de delegado conveniente al que está acostumbrado cuando se trata de vistas de tabla regulares. Echale un vistazo:

//  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 desea usar esto en un UITableViewController, probablemente necesite subclasificar y cumplir con el nuevo protocolo.

Funciona muy bien para mí, ¡espero que ayude a otros!

Delaware.
fuente
Increíble uso de patrones de delegación y categoría
valeCocoa
5

Respuesta rápida 3, utilizando la sintaxis moderna, incorporando otras respuestas y eliminando el código innecesario.

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

Simplemente agregue UILongPressGestureRecognizer a la celda prototipo dada en el guión gráfico, luego arrastre el gesto al archivo .m de viewController para crear un método de acción. Lo hice como dije.

DawnSong
fuente
¿Puedes explicar un poco más? ¿Hiciste de la celda prototipo una propiedad en tu VC?
Ethan Parker
-2

Use la propiedad de marca de tiempo de UITouch en toques Comenzó para iniciar un temporizador o detenerlo cuando toques Ended fue despedido

Thomas Joulin
fuente
Gracias por su respuesta, pero ¿cómo puedo detectar qué fila está afectada por el toque?
FoOg
Podría estar equivocado, pero no se proporciona nada para ayudarlo a hacer eso. Tendrá que obtener los índices de las celdas visibles actuales con [tableView indexPathsForVisibleRows] y luego, utilizando algunos cálculos (su desplazamiento tableView desde la parte superior + X veces las filas) sabrá que las coordenadas de su dedo están en fila.
Thomas Joulin el
Estoy seguro de que hay una manera más fácil de hacerlo, de todos modos si tienes otra idea, estaré aquí :)
foOg
Me gustaría saber si algo más fácil es posible. Pero no creo que lo haya, sobre todo porque Apple no quiere que manejemos las interacciones ... Parece una forma de Android de pensar este "menú de acceso rápido". Si fuera mi aplicación, la manejaré como la aplicación de Twitter. Un deslizamiento hacia la izquierda muestra las opciones
Thomas Joulin,
Sí, pensé en eso, así que si realmente no puedo hacerlo con un evento de prensa prolongado, usaré el método de deslizamiento. Pero, tal vez alguien en el desbordamiento de pila lo hizo ...
foOg