2010-10-13 15 views
165

Me gustaría manejar una pulsación larga en un UITableViewCell para imprimir un "menú de acceso rápido". ¿Alguien ya hizo esto?Pulsación larga en UITableView

Particularmente el gesto reconoce en UITableView?

Respuesta

403

Primera añadir el reconocedor gesto 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]; 

A continuación, en el controlador gesto :

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

hay que tener cuidado con esto para que no interfiera con el golpeteo normal del usuario de la célula y también cuenta t sombrero handleLongPress puede disparar varias veces (esto se debe a los cambios de estado del reconocedor de gestos).

+1

Awesome !!! ¡Muchas gracias! Pero una última pequeña pregunta: ¿Por qué el método handleLongPress es llamada cuando el toque finalizó? – foOg

+0

No se llama cuando el toque finaliza, pero puede disparar varias veces si el usuario mantiene el dedo sobre la celda durante más de 4 segundos (en este ejemplo) antes de levantarlo. – Anna

+105

Corrección: se dispara varias veces para indicar los diferentes estados del gesto (comenzó, cambió, terminó, etc.). Por lo tanto, en el método del controlador, compruebe 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) ...'. – Anna

-2

Utilice la propiedad de marca de tiempo UITouch en touchesBegan para poner en marcha un temporizador o detenerlo cuando touchesEnded fue despedido

+0

Gracias por su respuesta, pero ¿cómo puedo detectar qué fila está preocupada por el tacto? – foOg

+0

Podría estar equivocado, pero no se proporciona nada para ayudarlo a hacer eso. Tendrás que obtener los índices de las celdas visibles actuales con [tableView indexPathsForVisibleRows] y luego, usando algunos cálculos (tu offset de tableView desde la parte superior + X veces las filas) sabrás que las coordenadas de tu dedo están sobre fila. –

+0

Estoy seguro de que hay una forma más fácil de hacerlo, de todos modos si tienes otra idea, estaré aquí :) – foOg

12

parece ser más eficaz que aporta el reconocedor directamente a la célula, como se muestra aquí:

Tap&Hold for TableView Cells, Then and Now

(desplazarse al ejemplo en la parte inferior)

+7

¿Cómo puede ser más eficiente la asignación de un nuevo objeto reconocedor de gestos para cada fila que un único reconocedor para toda la tabla? – user2393462435

+6

Recuerde que solo hay unas pocas celdas creadas si dequeue funciona correctamente. – Ants

42

He usado de Anna-Karenina responde, y funciona casi genial con un error muy serio.

Si está utilizando secciones, presionar prolongadamente el título de la sección le dará un resultado incorrecto al presionar la primera fila en esa sección, he agregado una versión fija a continuación (incluido 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); 
      } 
     } 
    } 
} 
+0

Hola, @marmor: Quiero preguntar ¿es posible identificar solo una parte de una vista que tocó el usuario? – Manthan

+0

Oye, puedes usar hitTest para eso (https://developer.apple.com/library/ios/documentation/uikit/reference/uiview_class/uiview/uiview.html#//apple_ref/occ/instm/UIView/hitTest: withEvent :). Verifique esta respuesta, por ejemplo, sobre cómo usar: http://stackoverflow.com/a/2793253/819355 – marmor

+0

Si bien la respuesta aceptada funciona. Registra múltiples incendios, junto con el registro mientras arrastra en la pantalla. Esta respuesta no. Una respuesta legítima de copiar y pegar. Gracias. – ChrisOSX

6

He reunido 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, como el que está acostumbrado cuando se trata de vistas de tabla regulares. Compruébelo usted mismo:

// 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 utilizar esto en un UITableViewController, es probable que tenga una subclase y se ajustan al nuevo protocolo.

Funciona muy bien para mí, ¡espero que ayude a los demás!

+0

¡genial! impresionante uso de categorías, gracias – MaKo

+0

Increíble uso de los patrones de delegación y categoría – valeCocoa

2

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

+0

¿Puedes explicar un poco más? ¿Hiciste que la célula prototipo fuera una propiedad en tu VC? –

16

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

Arrastra un largo Presiona Gesture Recognizer y suéltalo en tu Table Cell. Saltará al final de la lista a la izquierda.

enter image description here

A continuación, conecte el reconocedor gesto de la misma forma que lo haría conectar un botón. enter image description here

Agregue el código de Marmor en el controlador de la 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); 
     } 
    } 
} 

}

+2

La mejor respuesta en mi opinión –

+3

Long Press Gesture Recognizer debe aplicarse a la vista de tabla no a la celda de vista de tabla. Dejarlo en la celda de vista de tabla solo tendrá la fila 0 escuchando la pulsación larga. – Alex

9

respuesta en Swift:

Añadir delegado UIGestureRecognizerDelegate a su UITableViewController.

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

} 
8

respuesta en Swift 3.0 (Continuatuin de la respuesta de Ricky en Swift)

Añadir la UIGestureRecognizerDelegate a su ViewController

override func viewDidLoad() { 
    super.viewDidLoad() 

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

Y la función:

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 == UIGestureRecognizerState.began) { 
     print("Long press on row, at \(indexPath!.row)") 
    } 
} 
0

Swift 3 respuesta, utilizando la sintaxis moderna, la incorporación de otras respuestas, y la eliminación de código que no sean necesarios.

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 
} 
Cuestiones relacionadas