volver sirven que por para los inicio gestos flotante control como boton atras assistive ios ios7 uigesturerecognizer gesture uitapgesturerecognizer
http://tech.octopod.com/test/BuggySample2.ziphttp://tech.octopod.com/test/BuggySample.zip

sirven - Problemas con el reconocedor de gestos en iOS 7



gestos iphone 7 (4)

Estoy agregando varios objetos UIView (por ejemplo, 5) a la pantalla, uno dentro de otro. Esto, por ejemplo, view5.superview = view4 , view4.superview = view3 , view3.superview=view2 , view2.superview = view1 . Para todos estos UIView puse uitapgesturerecognizer; para view1-4 Simplemente hago NSLog (@ "tap% @", self) en la devolución de llamada, mientras que para view5 toca configuro lo siguiente: eliminar view4 de la jerarquía, luego colocar el mismo objeto view4 ''en el mismo lugar de la jerarquía . Este objeto también contiene view5 ''para el cual se configura UITapGestureRecognizer (prácticamente, reemplazo una parte del marcado con una similar).

Entonces empiezo a hacer clic en view5. Algún tiempo, view5 sigue captando su toque y todo está bien, pero un número aleatorio de toques más tarde (cada vez que este número es diferente) uno de los objetos de view1-4 comienza a capturar este toque, aunque todavía estamos haciendo clic en view5. Todo el problema tiene un carácter aleatorio: a veces ocurre en el décimo lanzamiento, a veces en el segundo. A veces, los objetos erróneos comienzan a atrapar toques en el primer toque Además, nunca sé qué objeto atrapará un toque, cuando todo sale mal. El marco para la vista (n + 1) se estableció, por ejemplo, como la mitad de la vista del marco (n), mientras que el marco para la vista 1 - por ejemplo (0,0 320, 460).

Todas las operaciones con objetos ui descritas anteriormente se llevan a cabo en el hilo principal, y todo lo que he contado funciona perfectamente en iOS 4.3 - 6.1 con ejemplos mucho más complejos. Pero el iOS7 hace de él una especie de infierno aleatorio.

Actualización: he creado un proyecto de ejemplo, para simplificar el proceso de depuración. No hay operaciones de agregar / eliminar subvistas en el tap. Solo 4 vistas en pantalla, al tocar la aplicación se registra la vista que se tocó. Por lo tanto, debe tocar en la vista más pequeña (4). Si ve "toque 4 toque 4 toque 4 ..." en el registro, este es el caso cuando todo funciona bien, deténgase y ejecute nuevamente, deténgase y ejecute nuevamente, deténgase y ejecute nuevamente, etc. Y en algunas ejecuciones (quizás después de 10) + ejecuciones exitosas) no verá "toque 4" en la primera línea, verá "toque 1" o "toque 2" o "toque 3", y continuará así, estos son los casos malos.

El proyecto de muestra se puede descargar desde aquí: http://tech.octopod.com/test/BuggySample.zip (solo 33 Kb en el archivo).

Actualización 2

Hemos publicado un error en Apple, lo publicaré aquí cuando recibamos algunos comentarios. Sin embargo, cualquier buena solución sería muy apreciada!

Actualización 3

La solución, proporcionada por Yuvrajsinh, está realmente trabajando en el proyecto de muestra. Desafortunadamente, todavía no ayuda a resolver el problema que ocurrió en el proyecto principal donde apareció inicialmente. La razón principal por ahora es que si alguna vista sin autogestos está sobre el contenido al que se puede hacer clic, el elemento de vista aleatoria debajo comienza a captar la interacción (en lugar de la parte superior con un conjunto de gestos de interacción. ¿Tiene alguna idea de cómo puede resolverse?) ? La muestra actualizada se puede descargar desde aquí: http://tech.octopod.com/test/BuggySample2.zip


Debido a que el problema solo ocurre en iOS 7, puede usar uno de los nuevos métodos de delegado para resolver el problema:

– gestureRecognizer:shouldRequireFailureOfGestureRecognizer: – gestureRecognizer:shouldBeRequiredToFailByGestureRecognizer:

Lo resolví mediante la implementación de gestureRecognizer:shouldBeRequiredToFailByGestureRecognizer para realizar un gestureRecognizer:shouldBeRequiredToFailByGestureRecognizer y "rastrear" la vista de supervisión de la vista del gesto para que pueda devolver "SÍ" si encuentro que el gesto de la vista es igual al proporcionado. Detallo mi resolución completa aquí: https://.com/a/19659848/1147934 .

Explicación
El problema con los reconocedores de gestos en iOS 7 es que el gesto de una vista previa recibe sus toques antes de que uno de sus gestos de subvista reciba sus toques. Esto hace que el gesto de supervisión vea que luego cancela el reconocedor de la vista secundaria ... esto es (¿incorrecto?) Y se han archivado varios errores con Apple. Se ha señalado que Apple no garantiza el orden en que los gestos reciben toques. Creo que muchos "nosotros" confiamos en un detalle de implementación que cambió en iOS 7. Por eso utilizamos los nuevos métodos de delegado, que parecen estar diseñados para ayudarnos a resolver este problema.

Nota: Hice pruebas exhaustivas utilizando mis propios reconocedores sub-asignados, registrando todos los toques y descubrí que la razón por la que fallan los reconocedores es porque los gestos de supervisión de la vista recibían toques antes de que el gesto de una subvista estuviera en aproximadamente el 5% de los casos. Cada vez que esto sucedía, se producía el fracaso. Esto sucede con más frecuencia si tiene jerarquías "profundas" con muchos gestos.

Los nuevos métodos de delegado pueden ser confusos, por lo que debe leerlos con cuidado.

Estoy usando el método (he cambiado el nombre de los argumentos para que sean más fáciles de entender)

– gestureRecognizer:(UIGestureRecognizer *)thisRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *) otherRecognizer .

Si devuelve "SÍ", entonces el reconocedor de gestos provisto, otro otherRecognizer , requerirá que este thisRecognizer falle antes de que pueda ser reconocido. Esta es la razón por la que, en mi respuesta, rastreo la jerarquía de supervisión para verificar si contiene una supervisión que tenga el otherRecognizer . Si lo hace, quiero que otro otherRecognizer requiera que este thisRecognizer falle porque este thisRecognizer está en una subvista y debería fallar antes de que se reconozca el gesto de supervisión. Esto asegurará que los gestos de subvista sean reconocidos antes que los gestos de supervisión. ¿Tener sentido?

Alternativa
Podría hacerlo al revés y usar:

– gestureRecognizer:(UIGestureRecognizer *)thisRecognizer shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherRecognizer

Ahora tendría que rastrear a través de toda mi jerarquía de subvistas , verificar si hay otro otherRecognizer en él y devolver YES si lo está. No utilizo este método porque rastrear toda la jerarquía de subvistas es mucho más difícil y costoso de hacer que verificar una jerarquía de supervisión. Rastrear una jerarquía de subvistas tendría que ser una función recursiva, mientras que puedo usar un simple bucle while para verificar una jerarquía de supervisión. Así que recomiendo el primer enfoque que describo.

¡Advertencia!
Tenga cuidado con el uso de gestureRecognizer:shouldReceiveTouch: El problema es un problema cuyo gesto recibe toques primero (cancelar el otro gesto) ... es un problema de resolución de conflictos. Si implementa gestureRecognizer:shouldReceiveTouch: se arriesga a rechazar un gesto de supervisión si el gesto de subvista falla porque tiene que adivinar cuándo se puede reconocer un gesto de subvista. Un gesto de subvista puede fallar legítimamente por razones distintas a las que los toques están fuera de los límites, por lo que tendría que conocer los detalles de la implementación para poder adivinar correctamente. Desea que el gesto de supervisión se reconozca cuando falle el gesto de subvista, pero de todos modos no debe saber con certeza si fallará antes de que falle. Si un gesto de subvista falla, normalmente desea que se reconozca el gesto de supervisión. Esta es la cadena de respondedores normal (supervisión de subvista) y si te metes en eso, podrías terminar con un comportamiento inesperado.


Establezca un delegado en el reconocedor e implemente gestureRecognizer:shouldReceiveTouch: reconocimiento gestureRecognizer:shouldReceiveTouch:

La implementación básicamente debería bloquear los toques en las subvistas, pero probablemente habrá algunos criterios adicionales de acuerdo con la configuración y la jerarquía de tu vista real.

La muestra a continuación simplemente comprueba si la vista de acierto es una subvista directa y si tiene algún reconocedor de gestos, en cuyo caso se le permite ver los toques.

Los toques de bloqueo deben ser más robustos que jugar con los estados del reconocedor, ya que no hay posibilidad de que las vistas no deseadas disparen a sus reconocedores. El requisito de implementar criterios personalizados es un inconveniente, pero, una vez más, creo que es más sólido implementar explícitamente el comportamiento cuando se resuelve un error de causa desconocida, como en este caso.

#import "View1234.h" @interface View1234 () <UIGestureRecognizerDelegate> @end @implementation View1234 - (id) initWithId:(NSString *)vid_ { if (self = [super init]) { vid = [vid_ retain]; UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] init]; tapGesture.numberOfTapsRequired = 1; tapGesture.numberOfTouchesRequired = 1; [tapGesture addTarget:self action:@selector(handleTap:)]; tapGesture.delegate = self; [self addGestureRecognizer:tapGesture]; [tapGesture release]; } return self; } //- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{ // // if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) { // if (self.tag==gestureRecognizer.view.tag) { // return YES; // } // } // // return NO; //} - (void) handleTap:(id)tap { NSLog(@"tap %@", vid); } - (void) dealloc { [vid release]; vid = nil; [super dealloc]; } - (BOOL)shouldReceiveTouchOnView:(UIView *)hitView { NSLog(@"Should view:/n%@/nreceive touch on view:/n%@", self, hitView); // Replace this implementation with whatever you need... // Here, we simply check if the view has a gesture recognizer and // is a direct subview. BOOL res = (hitView.gestureRecognizers.count == 0 && [self.subviews containsObject:hitView]); NSLog(@"%@", res? @"YES":@"NO"); return res; } #pragma mark - Gesture Recognizer Delegate - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch { UIView *hitView = [self hitTest:[touch locationInView:self.superview] withEvent:nil]; if (hitView == self) { NSLog(@"Touch not in subview"); return YES; } return [self shouldReceiveTouchOnView:hitView]; } @end


He hecho algunos cambios en su código y también lo he probado mucho y el problema no se está generando.

Al crear la vista, establezco una etiqueta en cada vista para distinguirla:

View1234 *v1 = [[View1234 alloc] initWithId:@"1"]; v1.tag =1; v1.backgroundColor = [UIColor redColor]; v1.frame = CGRectMake(0, 0, 320, 460); View1234 *v2 = [[View1234 alloc] initWithId:@"2"]; v2.tag=2; v2.backgroundColor = [UIColor greenColor]; v2.frame = CGRectMake(0, 0, 160, 230); View1234 *v3 = [[View1234 alloc] initWithId:@"3"]; v3.tag=3; v3.backgroundColor = [UIColor blueColor]; v3.frame = CGRectMake(0, 0, 80, 115); View1234 *v4 = [[View1234 alloc] initWithId:@"4"]; v4.tag=4; v4.backgroundColor = [UIColor orangeColor]; v4.frame = CGRectMake(0, 0, 40, 50);

View1234.h

@interface View1234 : UIView <UIGestureRecognizerDelegate> { NSString *vid; } - (id) initWithId:(NSString *)vid_; @end

Y siguiente es el código completo de View1234.m

- (id) initWithId:(NSString *)vid_ { if (self = [super init]) { vid = [vid_ retain]; UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] init]; tapGesture.delegate = self; tapGesture.numberOfTapsRequired = 1; tapGesture.numberOfTouchesRequired = 1; [tapGesture addTarget:self action:@selector(handleTap:)]; [self addGestureRecognizer:tapGesture]; [tapGesture release]; } return self; } - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{ if (touch.view==self ) { return YES; } else if ([self.subviews containsObject:touch.view] && ![touch.view isKindOfClass:[self class]]) { return YES; } return NO; } /*- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{ if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) { if (self.tag==gestureRecognizer.view.tag) { return YES; } } return NO; }*/ - (void) handleTap:(UITapGestureRecognizer *)tap { NSLog(@"tap %@", vid); } - (void) dealloc { [vid release]; vid = nil; [super dealloc]; }

ACTUALIZACIÓN : ¿Por qué este problema viene en realidad.

Cuando agrega una UIView como una subvista de otra UIView con UITapGestureRecognizer en cada vista, en algún caso UITapGestureRecognizer estado de UITapGestureRecognizer se falla de alguna manera ( lo he depurado más de 50 veces y llego a saber esto ). Entonces, cuando cualquier subvista de cualquier vista no puede manejar el gesto de toque, entonces el sistema pasará el gesto a su súper vista para manejar ese gesto, y esto continúa.

Si realiza la depuración, llegará a saber que se llama varias veces a gestureRecognizerShouldBegin según la jerarquía de la vista. En este caso en particular, si view3 en view3 continuación, se llamará a view3 de view3 de view3 de view3 3 veces ya que la view3 encuentra en el tercer nivel de la jerarquía de vista, por lo que se llamará a view3, view2 and view1 de view3, view2 and view1 de view3, view2 and view1 de view3, view2 and view1 para view3, view2 and view1 .

Por lo tanto, para resolver el problema, gestureRecognizerShouldBegin YES gestureRecognizerShouldBegin de gestureRecognizerShouldBegin para la vista correcta y NO para el resto, por lo que resuelve el problema.

ACTUALIZACIÓN 2 : He hecho algún cambio en el código en mi respuesta editada y espero que resuelva su problema. Y también gracias a @masmor, también encontré alguna pista de su respuesta para resolver problemas.


No he probado tu proyecto o el siguiente.

Debería poder usar gestos de gestureRecognizerShouldBegin: para evitar que cualquier gesto que no pertenezca a la vista se dispare cuando se toca la vista.

Podría hacer eso con una subclase de UIView , o podría crear una categoría en UIView (con una propiedad u objeto asociado) que agregue una bandera para determinar qué debería hacer cada instancia de vista. Esto romperá algunos tipos de vista, así que tenga cuidado.

Eso no ayudará si el problema es el orden de las vistas ...