UITableView dentro de UIScrollView no recibe la primera pulsación después de desplazarse

Breve

Tengo un problema con una UITableView dentro de UIScrollView . Cuando scrollView el scrollView externo, la table no recibe el evento willSelect/didSelect en el primer toque, pero sí en el segundo. Lo que es aún más extraño, la celda en sí recibe los toques y el estado resaltado, incluso cuando el delegate no lo hace.

Explicación detallada

Mi vista jerarquía:

 UIView - UIScrollView (outerscroll) - Some other views and buttons - UITableView (tableView) 

Dentro de la vista de desplazamiento tengo algunas vistas adicionales que se expanden / cierran dinámicamente. La vista de tabla debe ser “arreglada” en la parte superior, junto con algunos otros elementos de la vista, así que es por eso que creé este diseño, que me permite mover elementos fácilmente de una manera similar a la recomendada por Apple mediante el uso de transformaciones cuando el rollo sucede.

La vista de tabla se transforma con un efecto de traducción cuando el desplazamiento externo se mueve así:

 - (void)scrollViewDidScroll:(UIScrollView *)scrollView { if (scrollView == self.outerScrollView) { CGFloat tableOffset = scrollView.contentOffset.y - self.fixedHeaderFrame.origin.y; if (tableOffset > 0) { self.tableView.contentOffset = CGPointMake(0, tableOffset); self.tableView.transform = CGAffineTransformMakeTranslation(0, tableOffset); } else { self.tableView.contentOffset = CGPointMake(0, 0); self.tableView.transform = CGAffineTransformIdentity; } // Other similar transformations are done here, but not involving the table } 

En mi celda, si implemento estos métodos:

 - (void)setSelected:(BOOL)selected { [super setSelected:selected]; if (selected) { NSLog(@"selected"); } } - (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated { [super setHighlighted:highlighted animated:animated]; if (highlighted) { NSLog(@"highlighted"); } } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [super touchesBegan:touches withEvent:event]; NSLog(@"touchesBegan"); } - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { [super touchesEnded:touches withEvent:event]; NSLog(@"touchesEnded"); } - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { [super touchesCancelled:touches withEvent:event]; NSLog(@"touchesCancelled"); } 

Y puede ver esta salida cuando falla (primer toque):

 2014-02-10 13:04:40.940 MyOrderApp[5588:70b] highlighted 2014-02-10 13:04:40.940 MyOrderApp[5588:70b] touchesBegan 2014-02-10 13:04:40.978 MyOrderApp[5588:70b] touchesEnded 

Y este cuando funciona (segundo toque):

 2014-02-10 13:05:30.359 MyOrderApp[5588:70b] highlighted 2014-02-10 13:05:30.360 MyOrderApp[5588:70b] touchesBegan 2014-02-10 13:05:30.487 MyOrderApp[5588:70b] touchesEnded 2014-02-10 13:05:30.498 MyOrderApp[5588:70b] expanded 

No se realiza ningún otro cambio de marco, animación o cualquier otra interacción de visualización entre el primer y el segundo toque. Además, solo cuando se desplazan grandes cantidades aparece el error, pero con desplazamientos de solo unos pocos píxeles todo sigue funcionando como se esperaba.

Experimenté cambiando algunas propiedades también, pero sin suerte. Algunas de las cosas que hice:

  • Eliminar userInteractionEnabled de vistas que no sean el desplazamiento y la tabla
  • Agregue una llamada a setNeedsLayout en la tabla, desplácese y vista principal cuando ocurre scrollViewDidScroll .
  • Eliminar las transformaciones de la tabla (aún sucede)

He visto algunos comentarios sobre el comportamiento inesperado de incrustar UITableViews dentro de UIScrollViews pero no veo esa advertencia en la documentación oficial de Apple, por lo que espero que funcione.

La aplicación es solo iOS7 +.

Preguntas

¿Alguien ha tenido problemas similares ? ¿Por qué es esto y cómo puedo resolverlo ? Creo que podría interceptar el gesto de toque en la celda y pasarlo con un delegado personalizado o similar, pero me gustaría que la tabla reciba los eventos adecuados y así mi UITableViewDelegate recibe como se esperaba.

Actualizaciones

  • Traté de desactivar la reutilización de la celda como se sugiere en un comentario, pero todavía sucede de la misma manera.

De Apple Documentation , no debe incrustar una UITableView dentro de UIScrollView .

Importante: no debe incrustar objetos UIWebView o UITableView en objetos UIScrollView. Si lo hace, puede producirse un comportamiento inesperado porque los eventos táctiles de los dos objetos pueden mezclarse y manejarse de forma incorrecta.

Su problema está realmente relacionado con lo que hace su UIScrollView .

Pero si es solo para ocultar la tabla cuando sea necesario (ese era mi caso), puede mover la UITableView en su superview.

Escribí un pequeño ejemplo aquí: https://github.com/rvirin/SoundCloud/

deje la propiedad scrollEnabled interior de scrollEnabled configurada como YES . esto permite que el UITableView interno sepa manejar los toques relacionados con el scroll en el UIScrollView correctamente.

¡Me encontré con este mismo problema y encontré una solución!

delaysTouchesBegan establecer delaysTouchesBegan en verdadero en su vista de desplazamiento para que la vista de desplazamiento envíe su gesto desplazado fallido (es decir, el tap) a sus elementos secundarios.

var delaysTouchesBegan: BoolUn valor booleano que determina si el receptor demora el envío de toques en una fase de inicio a su vista.

Cuando el valor de la propiedad es SÍ, la ventana suspende la entrega de objetos táctiles en la fase UITouchPhaseBegan a la vista. Si el reconocedor de gestos reconoce posteriormente su gesto, estos objetos táctiles se descartan. Si el reconocedor de gestos, sin embargo, no reconoce su gesto, la ventana entrega estos objetos a la vista en un mensaje touchesBegan: withEvent: (y posiblemente un mensaje de seguimiento touches: withEvent: para informarle de las ubicaciones actuales de los toques) .

https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIGestureRecognizer_Class/index.html#//apple_ref/occ/instp/UIGestureRecognizer/delaysTouchesBegan

Pero hay una trampa … ¡no funciona si lo haces directamente en la vista de desplazamiento!

 // Does NOT work self.myScrollview.delaysTouchesBegan = true 

Aparentemente este es un error de iOS donde configurar esta propiedad no funciona (gracias apple). Sin embargo, hay una solución simple: establecer la propiedad directamente en el gesto panorámico de scrollview. Efectivamente, esto funcionó para mí a la perfección:

 // This works!! self.myScrollview.panGestureRecognizer.delaysTouchesBegan = true 

Parece que su UiTableView no reconoce su toque. ¿Trataste de usar eso?

 - (BOOL)gestureRecognizer:(UIPanGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UISwipeGestureRecognizer *)otherGestureRecognizer { if ([otherGestureRecognizer.view isKindOfClass:[UITableView class]]) { return YES; } return NO; } 

Nota de manzana:

se llama cuando el reconocimiento de uno de gestureRecognizer u otroGestureRecognizer sería bloqueado por el otro. devuelva SÍ para permitir que ambos lo reconozcan simultáneamente. la implementación predeterminada devuelve NO (de manera predeterminada, no se pueden reconocer dos gestos simultáneamente)

nota: se garantiza el retorno de SÍ para permitir el reconocimiento simultáneo. NO se garantiza que el NO devuelva el reconocimiento simultáneo, ya que el delegado del otro gesto puede regresar SÍ

Espero que eso ayude.

Los reconocedores de gestos no funcionarán correctamente para dos vistas de desplazamiento incrustadas o subclases.

Pruebe una solución alternativa:

  1. Utilice transparencias, personalizaciones y superposición de todo en UIButton celda con la etiqueta adecuada, UIButton subclase y agregue una propiedad de ruta de índice y sobrescriba cada vez en una celda reutilizada.

  2. Agregue este botón como una propiedad a su celda personalizada.

  3. Agregue el destino para UIControlEvent deseado (uno o más) que apunte a su clase de adopción de protocolo UITableViewDelegate.

  4. Desactive la selección en IB y administre manualmente la selección desde el código.

Esta solución requiere atención para casos de selección individual / múltiple.

Me encontré con una UITableView con scrollEnabled como NO dentro de UIScrollView en algún código heredado. No he podido cambiar fácilmente la jerarquía existente ni habilitar el desplazamiento, pero propongo la siguiente solución alternativa para el problema del primer toque:

 @interface YourOwnTableView : UITableView @end @implementation YourOwnTableView - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { [super touchesCancelled:touches withEvent:event]; // Note that this is a hack and it can stop working at some point. // Looks like a table view with scrollEnabled being NO does not handle cancellation cleanly, // so let's repeat begin/end touch sequence here hoping it'll reset its own internal state properly // but won't trigger cell selection (the touch passed is in its cancelled phase, perhaps there is a part // of code inside which actually checks it) [super touchesBegan:touches withEvent:event]; [super touchesEnded:touches withEvent:event]; } @end 

Una vez más, esto es solo una solución trabajando en mi caso específico. Tener una vista de tabla dentro de una vista de desplazamiento sigue siendo una cosa incorrecta.

Recomendaría buscar opciones como no dejar que su celda esté en estado resaltado cuando en realidad está desplazando la vista de desplazamiento exterior, que es muy fácil de manejar y es la forma recomendada. Puede hacer esto simplemente tomando un booleano y alterándolo en el siguiente método

 - (void)scrollViewDidScroll:(UIScrollView *)scrollView 

La vista de desplazamiento está tratando de averiguar si la intención del usuario es desplazarse o no, por lo que está retrasando el toque inicial a propósito. Puede desactivarlo configurando delaysContentTouches en NO .

Tengo el mismo problema con UITableView nested y he encontrado una solución alternativa para esto:

 innerTableView.scrollEnabled = YES; innerTableView.alwaysBounceVertical = NO; 

Tendrá que establecer el alto de la vista de tabla interna para que coincida con la altura total de sus celdas para que no se desplace cuando el usuario desplaza la vista externa.

Espero que esto ayude.

Mi error fue implementar el método de delegado:

 - (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath 

en lugar de la que quería implementar:

 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 

Por lo tanto, solo se llama a la segunda celda que se toca, porque fue entonces cuando se seleccionó la primera celda. Error estúpido hecho con la ayuda de autocompletar. Solo un pensamiento para aquellos de ustedes que pueden vagar por aquí sin darse cuenta de que han cometido el mismo error también.

Como otras respuestas dicen que no debe poner una vista de tabla en una vista de desplazamiento. Una UITableView hereda de UIScrollView de todos modos, así que supongo que es ahí donde las cosas se vuelven confusas. Lo que siempre hago en esta situación es:

1) Subclass UITableViewController e incluye una propiedad UIView * headView.

2) En el UIViewController padre crear todas las cosas superiores en un contenedor UIView

3) Inicialice su UITableView personalizado y agregue la vista de tableView al controlador de vista de tamaño completo

 [self.view addSubview: self.myTableView.view]; 

4) Configure el headView para que sea su UIView gubbins

 self.tableView.headView = myHeadViewGubbins. 

5) En el método tableViewController

 -(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger *)section; 

Hacer:

 if ( section == 0 ) { return self.headView; } 

Ahora tiene una vista de tabla con un montón de otros shizzle en la parte superior.

¡Disfrutar!

Que sea así, si la vista de tabla táctil funcionará correctamente. también con vista de desplazamiento en el mismo controlador de vista también.

 tableview.scrollEnabled = true; 

Tengo el mismo problema, luego consulte “Anidar vistas de desplazamiento” como dijo lxx.

https://developer.apple.com/library/ios/documentation/WindowsViews/Conceptual/UIScrollView_pg/NestedScrollViews/NestedScrollViews.html

Un ejemplo de desplazamiento direccional cruzado se puede encontrar en la aplicación Stocks. La vista superior es una vista de tabla, pero la vista inferior es una vista de desplazamiento horizontal configurada mediante el modo de paginación . Si bien dos de sus tres subvistas son vistas personalizadas, la tercera vista (que contiene los artículos de noticias) es una UITableView (una subclase de UIScrollView) que es una subvista de la vista de desplazamiento horizontal. Después de desplazarse horizontalmente a la vista de noticias, puede desplazar su contenido verticalmente.

Eso es trabajo

  1. Coloque un UIButton sobre su UITableViewCell y cree la salida como “btnRowSelect”.
  2. En su controlador de vista, coloque este código en cellForRowAtIndexPath

     cell.btnRowSelect.tag = indexPath.row cell.btnRowSelect.addTarget(self, action: Selector("rowSelect:"), forControlEvents: .TouchUpInside) 
  3. Agregue esta función a su viewController también-

     func rowSelect (sender:UIButton) { // "sendet.tag" give you the selected row // do whatever you want to do in didSelectRowAtIndexPath } 

Esta función “rowSelect” funcionará como lo hizo SelectRowAtIndexPath donde se obtiene la fila “indexPath.row” como “sender.tag”