UITapGestureRecognizer rompe UITableView didSelectRowAtIndexPath


He escrito mi propia función para desplazar los campos de texto hacia arriba cuando aparece el teclado. Para descartar el teclado pulsando lejos del campo de texto, he creado un UITapGestureRecognizer que se encarga de renunciar al primer respondedor en el campo de texto al pulsar lejos.

Ahora también he creado un autocompletado para el campo de texto que crea un UITableView justo debajo del campo de texto y lo rellena con elementos a medida que el usuario ingresa el texto.

Sin embargo, al seleccionar una de las entradas en el auto completed table, didSelectRowAtIndexPath no se llama. En su lugar, parece que el reconocedor de gestos tap está siendo llamado y simplemente renuncia al primer respondedor.

Supongo que hay alguna manera de decirle al reconocedor de gestos tap que siga pasando el mensaje tap a UITableView, pero no puedo averiguar qué es. Cualquier ayuda sería muy apreciada.

Author: KlimczakM, 2011-11-19

15 answers

Ok, finalmente lo encontré después de algunas búsquedas a través de los documentos de gesture recognizer.

La solución fue implementar UIGestureRecognizerDelegate y agregar lo siguiente:

////////////////////////////////////////////////////////////
// UIGestureRecognizerDelegate methods

#pragma mark UIGestureRecognizerDelegate methods

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    if ([touch.view isDescendantOfView:autocompleteTableView]) {

        // Don't let selections of auto-complete entries fire the 
        // gesture recognizer
        return NO;
    }

    return YES;
}

Que se hizo cargo de ella. Esperemos que esto ayude a otros también.

 230
Author: Jason,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-16 21:30:16

La forma más fácil de resolver este problema es:

UITapGestureRecognizer *tapRec = [[UITapGestureRecognizer alloc] 
    initWithTarget:self action:@selector(tap:)];
[tapRec setCancelsTouchesInView:NO];

Esto permite que el UIGestureRecognizer reconozca el toque y también pase el toque al siguiente respondedor. Una consecuencia no deseada de este método es si tiene un UITableViewCell en pantalla que empuja otro controlador de vista. Si el usuario toca la fila para descartar el teclado, se reconocerán tanto el teclado como el push. Dudo que esto sea lo que pretende, pero este método es adecuado para muchas situaciones.

También, ampliando la respuesta de Robert, si tiene un puntero a la vista de tabla en cuestión, entonces puede comparar directamente su clase en lugar de tener que convertir a una cadena y esperar que Apple no cambie la nomenclatura:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer 
     shouldReceiveTouch:(UITouch *)touch
{
    if([touch.view class] == tableview.class){
        return //YES/NO
    }

    return //YES/NO

}

Recuerde, también debe declarar el UIGestureRecognizer para tener un delegado con este código en él.

 199
Author: TMilligan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-30 15:02:23

Establece cancelsTouchesInView de tu recognizer en false. De lo contrario," consume " el toque por sí mismo, y no lo pasa a la vista de tabla. Es por eso que el evento de selección nunca ocurre.

, Por ejemplo, en swift

let tapOnScreen: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "CheckTheTime")
tapOnScreen.cancelsTouchesInView = false
view.addGestureRecognizer(tapOnScreen)
 53
Author: Abdulrahman Masoud,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-21 13:32:12

Y para Swift (basado en la respuesta de @ Jason):

class MyAwesomeClass: UIViewController, UIGestureRecognizerDelegate

private var tap: UITapGestureRecognizer!

override func viewDidLoad() {
   super.viewDidLoad()

   self.tap = UITapGestureRecognizer(target: self, action: "viewTapped:")
   self.tap.delegate = self
   self.view.addGestureRecognizer(self.tap)
}

// UIGestureRecognizerDelegate method
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
    if touch.view?.isDescendantOfView(self.tableView) == true {
        return false
    }
    return true
}
 28
Author: Nikolaj Nielsen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-04 14:24:22

Puedo tener una mejor solución para agregar un gesto de toque sobre una vista de tabla, pero permitiendo la selección de celdas al mismo tiempo:

func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
    if gestureRecognizer is UITapGestureRecognizer {
        let location = touch.locationInView(tableView)
        return (tableView.indexPathForRowAtPoint(location) == nil)
    }
    return true
}

Solo busco una celda en el punto de la pantalla donde el usuario está tocando. Si no se encuentra ninguna ruta de índice, entonces dejo que el gesto reciba el toque, de lo contrario, lo cancelo. Para mí funciona muy bien.

 17
Author: bonokite,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-11-28 10:46:11

Una solución similar es implementar gestureRecognizer:shouldReceiveTouch: usando la clase de la vista para determinar qué acción tomar. Este enfoque tiene la ventaja de no enmascarar los taps en la región que rodea directamente la tabla (las vistas de estas áreas aún descienden de las instancias UITableView, pero no representan celdas).

Esto también tiene una ventaja que funciona con varias tablas en una sola vista (sin agregar código adicional).

Advertencia: hay una suposición de que Apple no cambiará el classname.

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    return ![NSStringFromClass([touch.view class]) isEqualToString:@"UITableViewCellContentView"];
}
 13
Author: Robert Altman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-08 19:47:26

Creo que no hay necesidad de escribir bloques de códigos simplemente establecer cancelsTouchesInView a false para tu objeto gestual , por defecto es true y solo tienes que configurarlo false. Si está usando UITapGesture object en su código y también está usando UIScrollView(tableview , collectionview), establezca esta propiedad false

let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.dismissKeyboard))
tap.cancelsTouchesInView = false
view.addGestureRecognizer(tap)
 9
Author: SHUBHAYAN KUNDU,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-15 10:04:01

Si bien es tarde y muchas personas encuentran que las sugerencias anteriores funcionan bien, no pude conseguir que los métodos de Jason o TMilligan funcionen.

Tengo una vista de tabla estática con varias celdas que contienen campos de texto que reciben entradas de Números usando solo el Teclado Numérico. Esto fue ideal para mí:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    if(![touch.view isKindOfClass:[UITableViewCell class]]){

        [self.firstTF resignFirstResponder];
        [self.secondTF resignFirstResponder];
        [self.thirdTF resignFirstResponder];
        [self.fourthTF resignFirstResponder];

        NSLog(@"Touches Work ");

        return NO;
    }
    return YES;
}

Asegúrese de que ha implementado esto <UIGestureRecognizerDelegate> en su .archivo h.

Esta línea ![touch.view isKindOfClass:[UITableViewCell class]] comprueba si se ha pulsado una TableViewCell y descarta cualquier teclado activo.

 1
Author: App Dev Guy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-28 03:54:53

La solución simple es usar instancias de UIControl en UITableViewCell para obtener toques. Puede agregar cualquier vista con userInteractionEnables = = NO a UIControl para obtener toques.

 1
Author: Andrew Romanov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-11 05:38:53

Aquí está mi solución, que vincula el reconocedor shouldReceiveTouch directamente a si el teclado se muestra.

En el delegado del reconocedor de gestos tap:

#pragma mark - UIGestureRecognizerDelegate

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    if ([PFXKeyboardStateListener sharedInstance].visible) {
        return YES;
    }

    return NO;
}

Y el PFXKeyboardStateListener.h:

@interface PFXKeyboardStateListener : NSObject
{
    BOOL _isVisible;
}

+ (PFXKeyboardStateListener *)sharedInstance;

@property (nonatomic, readonly, getter=isVisible) BOOL visible;

@end

Y el PFXKeyboardStateListener.m:

static PFXKeyboardStateListener *sharedInstance;

@implementation PFXKeyboardStateListener

+ (PFXKeyboardStateListener *)sharedInstance
{
    return sharedInstance;
}

+ (void)load
{
    @autoreleasepool {
        sharedInstance = [[self alloc] init];
    }
}

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (BOOL)isVisible
{
    return _isVisible;
}

- (void)didShow
{
    _isVisible = YES;
}

- (void)didHide
{
    _isVisible = NO;
}

- (id)init
{
    if ((self = [super init])) {
        NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
        [center addObserver:self selector:@selector(didShow) name:UIKeyboardDidShowNotification object:nil];
        [center addObserver:self selector:@selector(didHide) name:UIKeyboardWillHideNotification object:nil];
    }
    return self;
}

@end

Es posible que desee actualizar el patrón de singleton del oyente de teclado, no he llegado a él todavía. Espero que esto funcione para todos los demás, así como funciona para mí. ^^

 0
Author: bgfriend0,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-23 17:12:01

Implementar este método para delegado de UIGestureRecognizer:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
       shouldReceiveTouch:(UITouch *)touch
{
  UIView *superview = touch.view;
  do {
    superview = superview.superview;
    if ([superview isKindOfClass:[UITableViewCell class]])
      return NO;
  } while (superview && ![superview isKindOfClass:[UITableView class]]);

  return superview != nil;
}
 0
Author: cxa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-05-02 06:43:13

En swift puedes usar esto dentro de

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    if CheckTheTime() == true {
        // do something 
    }else{
    }
}

func CheckTheTime() -> Bool{
    return true
}
 0
Author: Abdulrahman Masoud,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-20 14:05:19

Mi caso fue diferente, incluyendo uisearchbar y uitableview on self.vista. Me quería despedir uisearchbar teclado tocando en la vista.

var tapGestureRecognizer:UITapGestureRecognizer?

override func viewWillAppear(animated: Bool) {
    tapGestureRecognizer = UITapGestureRecognizer(target: self, action:Selector("handleTap:"))
}

En los métodos Delegados de UISearchBar:

func searchBarShouldBeginEditing(searchBar: UISearchBar) -> Bool {
    view.addGestureRecognizer(tapGestureRecognizer!)
    return true
}
func searchBarShouldEndEditing(searchBar: UISearchBar) -> Bool {
    view.removeGestureRecognizer(tapGestureRecognizer!)
    return true
}

Cuando el usuario toca uno mismo.vista:

func handleTap(recognizer: UITapGestureRecognizer) {
    sampleSearchBar.endEditing(true)
    sampleSearchBar.resignFirstResponder()
}
 0
Author: A.G,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-01 06:17:22

Esta es mi solución basada en las respuestas anteriores... A mí me ha funcionado...

//Create tap gesture for menu transparent view
UITapGestureRecognizer *rightTableTransparentViewTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(rightTableTransparentViewTapMethod:)];
[rightTableTransparentViewTap setCancelsTouchesInView:NO];
[_rightTableTransparentView addGestureRecognizer:rightTableTransparentViewTap];

- (void)rightTableTransparentViewTapMethod:(UITapGestureRecognizer *)recognizer {
    //Write your code here
}
 0
Author: iOS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-12 06:31:26

PROBLEMA: En mi caso, el problema fue que originalmente coloqué un botón en cada celda de collectionView y establecí las restricciones para llenar la celda, de modo que cuando se hizo clic en la celda, haría clic en el botón, sin embargo, la función de botones estaba vacía, por lo que nada parecía estar sucediendo.

FIX: Arreglé esto quitando el botón de la celda de la vista de colección.

 0
Author: Marcus Levi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-14 11:26:24