Reenviar UIGesture a vistas detrás

Estoy trabajando en una aplicación de iPhone (iOS 4.0 o posterior) y tengo algunos problemas con el manejo táctil entre varias vistas. Estoy teniendo una estructura de vista como esta

---> A superView | ---> SubView - A | ---> SubView - B (exactly on top of A, completely blocking A). 

enter image description here

Básicamente tengo una supervista, y las subvistas de hermanos A y B. B tiene el mismo marco que A, por lo tanto, oculta A por completo.

Ahora mi requisito es esto.

  1. La subvista B debe recibir todos los gestos deslizar y tocar (simple y doble).
  2. SubView A debe recibir todos los gestos de pellizco.

Así es como agregué los reconocedores de gestos a las vistas

 UISwipeGestureRecognizer *leftSwipe = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(leftSwipe:)]; [leftSwipe setDirection:(UISwipeGestureRecognizerDirectionLeft)]; leftSwipe.delegate = self; [bView addGestureRecognizer:leftSwipe]; [leftSwipe release]; UISwipeGestureRecognizer *rightSwipe = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(rightSwipe:)]; [rightSwipe setDirection:(UISwipeGestureRecognizerDirectionRight)]; rightSwipe.delegate = self; [bView addGestureRecognizer:rightSwipe]; [rightSwipe release]; UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(handlePinch:)]; pinch.delegate = self; [aView addGestureRecognizer:pinch]; [pinch release]; 

Hice algunas investigaciones y para mí UIGestureRecognizerDelegate parecía prometedor y implementé el método de delegado

 - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer 

Devolví NO para SubView B, esperando que la vista subyacente obtenga este evento. Sin embargo, tal suerte

 - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{ if ([gestureRecognizer isKindOfClass:[UIPinchGestureRecognizer class]] && [gestureRecognizer view] == bView) { NSLog(@"pinchGesture"); //bView.userInteractionEnabled = NO; return NO; } return YES; } 

Luego deshabilité la interacción del usuario de SubView B dentro de la callback del delegado (código comentado en el bloque de arriba), cuando se reconoce el gesto de pellizco, esperando que la parte restante del gesto sea recibida por SubView A. No hubo tanta suerte allí también.

Así que ahí es donde estoy ahora. He visto esta pregunta, donde la respuesta aceptada implica la propiedad cancelsTouchesInView . Pero creo que cancelsTouchesInView solo cancela un evento táctil en particular, no lo reenvíe.

¿Alguna otra forma de lograr mi requerimiento? Estoy listo para trabajar en cualquier sugerencia que proporciones.

EDITAR: TIEMPO DE BOUNTY

Mi llamado subView A es en realidad una instancia de la clase de vista de una biblioteca de un tercero, que quita todos los toques y no tengo ningún control sobre ningún gesto sobre él. Quiero una implementación diferente para el deslizamiento hacia la izquierda y hacia la derecha y quiero que pellizque, toque, etc. funcione igual que está funcionando con esta vista de terceros. Así que puse una vista en la parte superior de A (subvista B) para obtener deslizamientos hacia la izquierda y hacia la derecha. Pero ahora quiero reenviar otros eventos de gestos a la biblioteca subyacente.

Si entiendo correctamente tu problema, puedes agregar otra vista clara con rect, igual que la vista A y B, e implementar todos los gestos en ella: cuando pellizques un gesto, controla la subvista A, al deslizar y tocar (individual y doble) gestos: controla la subvista B. Puedes hacerlo de diferentes maneras: mediante punteros o simplemente enviando gestos recibidos al método en clase, que controla tu subvista.

por ejemplo:

 UISwipeGestureRecognizer *leftSwipe = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(leftSwipe:)]; [leftSwipe setDirection:(UISwipeGestureRecognizerDirectionLeft)]; leftSwipe.delegate = subViewAcontroller; [clearView addGestureRecognizer:leftSwipe]; [leftSwipe release]; UISwipeGestureRecognizer *rightSwipe = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(rightSwipe:)]; [rightSwipe setDirection:(UISwipeGestureRecognizerDirectionRight)]; rightSwipe.delegate = subViewAcontroller; [clearView addGestureRecognizer:rightSwipe]; [rightSwipe release]; UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(handlePinch:)]; pinch.delegate = subViewBcontroller; [clearView addGestureRecognizer:pinch]; [pinch release]; 

o:

 - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{ if ([gestureRecognizer isKindOfClass:[UIPinchGestureRecognizer class]]) { NSLog(@"pinchGesture"); [subViewBcontroller solvePinchGesture: gestureRecognizer]; } //etc return YES; } 

El gesto de pellizco nunca será recibido por aView, porque la jerarquía de vista es incorrecta.

 ---> A superView | ---> SubView - A | ---> SubView - B (exactly on top of A, completely blocking A). 

bView capturará todos los toques múltiples, incluido el gesto de pellizco, pero bView no manejará el gesto de pellizco, por lo que pasará al siguiente respondedor en la cadena Responder, es decir, la supervista de bView o el control de vista de bView. El evento de pellizco nunca pasará a sus vistas de hermanos, porque están en paralelo (sin relación padre-hijo o view-viewController).

Puede cambiar su jerarquía de vista de la siguiente manera:

 ---> A superView | ---> SubView - A | ---> SubView - B (child view of b, exactly on top of A, completely blocking A) - (void)viewDidLoad { [super viewDidLoad]; aView = [[UIView alloc] initWithFrame:self.view.bounds]; aView.backgroundColor = [UIColor blueColor]; bView = [[UIView alloc] initWithFrame:self.view.bounds]; bView.backgroundColor = [UIColor redColor]; [self.view addSubview:aView]; [aView addSubview:bView]; UISwipeGestureRecognizer *leftSwipe = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(leftSwipe:)]; [leftSwipe setDirection:(UISwipeGestureRecognizerDirectionLeft)]; leftSwipe.delegate = self; [bView addGestureRecognizer:leftSwipe]; UISwipeGestureRecognizer *rightSwipe = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(rightSwipe:)]; [rightSwipe setDirection:(UISwipeGestureRecognizerDirectionRight)]; rightSwipe.delegate = self; [bView addGestureRecognizer:rightSwipe]; UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(handlePinch:)]; pinch.delegate = self; [aView addGestureRecognizer:pinch]; } 

Obtenga más información del siguiente enlace: https://developer.apple.com/library/ios/#documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/EventsiPhoneOS/EventsiPhoneOS.html#//apple_ref/doc/uid/TP40009541-CH2-SW1

-> Nota importante del evento que pasa en la cadena de respuesta “Cuando el sistema entrega un evento táctil, primero lo envía a una vista específica. Para los eventos táctiles, esa vista es la que devuelve hitTest: withEvent :; para” temblar ” “Eventos de movimiento, eventos de control remoto, mensajes de acción y mensajes del menú de edición, esa vista es la primera respuesta. Si la vista inicial no maneja el evento, viaja por la cadena de respuesta a lo largo de una ruta particular:

  1. La vista de prueba o la primera respuesta pasa el evento o mensaje a su controlador de vista si tiene uno; si la vista no tiene un controlador de vista, pasa el evento o mensaje a su supervista.
  2. Si una vista o su controlador de vista no puede manejar el evento o mensaje, lo pasa a la supervista de la vista.
  3. Cada supervista posterior en la jerarquía sigue el patrón descrito en los primeros dos pasos si no puede manejar el evento o mensaje.
  4. La vista superior en la jerarquía de vista, si no maneja el evento o mensaje, la pasa al objeto ventana para su manejo.
  5. El objeto UIWindow, si no maneja el evento o mensaje, lo pasa al objeto de aplicación singleton “.

mi respuesta anterior no es la única solución, lo que debes hacer es deslizar y pellizcar las lógicas de manejo de gestos en la misma cadena de respuesta, de modo que el evento de gesto de pellizco sin control se pase al respondedor correcto. Por ejemplo, puede omitir la subvista b y agregar los gestos deslizar hacia la izquierda / derecha a superView of aView.

Hice algo similar hace algún tiempo. Quería hacer dos cosas diferentes con un dedo y dos o más dedos. Mi solución puede necesitar algún ajuste para usted y no puedo asegurar que todo funcione exactamente igual en su entorno de código. No encontré la documentación adecuada de hitTest y por qué se llama varias veces seguidas. Así que hice algunas pruebas y llegué a una solución, cómo separar los gestos de uno y varios dedos, lo que funcionó muy bien para mis necesidades.

Tenía esta jerarquía de vista:

 ---> A superView | ---> ScrollView - A - all touches | ---> ScrollView - B - only two+ finger scroll (exactly on top of A, completely blocking A). 

Implementé el interruptor en hitTest de la supervista:

 BOOL endDrag; // indicates scrollviewB did finish BOOL shouldClearHitTest; // bool to help calling the method clearHitTest only once. BOOL multiTouch; // needed for the method clearHitTest to know if a multi-touch is detected or not NSMutableArray *events; // store all events within one eventloop/touch; - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { if (!endDrag) { if (!shouldClearHitTest) { // as hitTest will be called multible times in one event-loop and i need all events, i will clean the temporaries when everything is done shouldClearHitTest = YES; [[NSRunLoop mainRunLoop] performSelector:@selector(clearHitTest) target:self argument:nil order:1 modes:[NSArray arrayWithObject:NSRunLoopCommonModes]]; } [events addObject:event]; // store the events so i can access them after all hittests for one touch are evaluated if (event.type == UIEventTypeTouches && ([_events count] > 3 || [[event allTouches] count] > 0 || _currentEvent)) { // two or more fingers detected. at least for my view hierarchy multiTouch = YES; return scrollViewB; } }else { endDrag = NO; } return scrollViewA; } - (void)clearHitTest { if (shouldClearHitTest) { shouldClearHitTest = NO; [events removeAllObjects]; if (multiTouch) { // Do some special stuff for multiTouch } multiTouch = NO; } } 

Le sugiero que use la siguiente línea de código cuando sea necesario para ayudarlo.

 //--->for bview gesture to detect swipe [pinch requireGestureRecognizerToFail:leftSwipe]; && [pinch requireGestureRecognizerToFail:rightSwipe]; //--->for aview gesture to detect pinch [rightSwipe requireGestureRecognizerToFail:rightSwipe]; || [leftSwipe requireGestureRecognizerToFail:pinch]; 

Si bView tiene gesto de pellizco. Desde la acción de gestos de pellizco de bView si se llama acción de gestos de pellizco de aView, entonces …

 UIPinchGestureRecognizer *pinchA = [[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(handlePinchA:)]; [aView addGestureRecognizer:pinchA]; [pinchA release]; UIPinchGestureRecognizer *pinchB = [[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(handlePinchB:)]; [bView addGestureRecognizer:pinchB]; [pinchB release]; -(void)handlePinchA:(UIGestureRecognizer*)gestureRecognizer { NSLog(@"handlePinch on A"); } -(void)handlePinchB:(UIGestureRecognizer*)gestureRecognizer { NSLog(@"handlePinch on B"); [self handlePinchA:gestureRecognizer]; } 

aView es una lib de terceros. ¿Puedes llamar al método handlePinchA desde afuera? NSLog gestureRecognizers me imprime action = handlePinchA

 NSLog(@"gestureRecognizers %@", [aView gestureRecognizers]); 

Me imprime:

 gestureRecognizers ( "; target= < (action=handlePinchA:, target=....