2011-03-07 21 views
72

Estoy tratando de averiguar cómo se hace esto de la manera correcta. He tratado de describir la situación: enter image description heresubvista de bloque UIGestureRecognizer para controlar eventos táctiles

estoy añadiendo un UITableView como subvista de un UIView. El UIView responde a un toque- y pinchGestureRecognizer, pero al hacerlo, la vista de tabla deja de reaccionar a esos dos gestos (aún reacciona a los golpes).

He hecho que funcione con el siguiente código, pero obviamente no es una buena solución y estoy seguro de que hay una mejor manera. Esto se pone en el UIView (el supervista):

-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { 
    if([super hitTest:point withEvent:event] == self) { 
     for (id gesture in self.gestureRecognizers) { 
      [gesture setEnabled:YES]; 
     } 
     return self; 
    } 
    for (id gesture in self.gestureRecognizers) { 
     [gesture setEnabled:NO]; 
    } 
    return [self.subviews lastObject]; 
} 

Respuesta

175

Tuve un problema muy similar y encontré mi solución in this SO question. En resumen, configúrese como el delegado para su UIGestureRecognizer y luego verifique la vista de destino antes de permitir que su reconocedor procese el toque. El método delegado relevante es:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer 
     shouldReceiveTouch:(UITouch *)touch 
+3

me gusta esta solución los más, ya que no implica jugar con los toques, 'hitTest: withEvent:' o 'pointInside: withEvent:'. – DarkDust

+6

Solución limpia, ya que puede probar con p. Ej. 'return! (touch.view == givenView);' si solo quiere excluir una vista dada o 'return! (touch.view.tag == kTagNumReservedForExcludingViews);' cuando quiera detener el procesamiento de su reconocedor toque una conjunto completo de diferentes subvistas. – cate

+6

Haría la prueba de aciertos con '- (BOOL) isDescendantOfView: (UIView *) view'. Esto también funciona bien en '- (void) touchesBegan: (NSSet *) toca withEvent: (UIEvent *) event' cuando se subclasifica UIGestureRecognizer. – Christoph

4

Una posibilidad es subclase reconocedor su gesto (si no lo ha hecho) y anular -touchesBegan:withEvent: tal que determina si cada contacto se inició en una vista secundaria y excluidos llama al -ignoreTouch:forEvent: para ese toque si lo hizo.

Obviamente, también deberá agregar una propiedad para realizar un seguimiento de la subvista excluida, o tal vez mejor, una matriz de subvistas excluidas.

+0

Hay montones de código aquí https://github.com/search?l=objective-c&q=uigesturerecognizer&ref=cmdform&type=Code – johndpope

92

El bloqueo de eventos táctiles a subvistas es el comportamiento predeterminado. Puede cambiar este comportamiento:

UITapGestureRecognizer *r = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(agentPickerTapped:)]; 
r.cancelsTouchesInView = NO; 
[agentPicker addGestureRecognizer:r]; 
+4

Esto enviará los eventos táctiles a las subvistas, pero también se enviará al reconocedor de gestos. Esto evitará el bloqueo de las subvistas, pero el reconocedor de gestos aún se reconocerá en las subvistas. –

+0

@Jonathan. Sí estoy de acuerdo con usted. Lo que hice en mi método gestor de gestos es comprobar si la ubicación del gesto ocurre dentro de la subvista en cuestión, en cuyo caso no necesito ejecutar el resto del código. Además, una razón por la que elegí esta solución alternativa es que UITapGestureRecognizer no declara el método 'translationInView'. Por lo tanto, la implementación del método UIGestureRecognizerDelegate mencionado anteriormente solo daría lugar a la falla con el error '... selector no reconocido enviado a blah'. Para verificar, use algo como: 'CGRectContainsPoint (subview.bounds, [recognizer locationInView: subview])'. – MkVal

+0

Según la pregunta de OP, esta respuesta debe seleccionarse como respuesta principal. – farzadshbfn

0

puede apagarlo y en .... en mi código que hice algo así como lo que necesitaba para apagarlo cuando el teclado no se muestra, se puede aplicar a su situación:

llamada esto es viewDidLoad etc:

NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; 
[center addObserver:self selector:@selector(notifyShowKeyboard:) name:UIKeyboardDidShowNotification object:nil]; 
[center addObserver:self selector:@selector(notifyHideKeyboard:) name:UIKeyboardWillHideNotification object:nil]; 

continuación, crear los dos métodos:

-(void) notifyShowKeyboard:(NSNotification *)inNotification 
{ 
    tap.enabled=true; // turn the gesture on 
} 

-(void) notifyHideKeyboard:(NSNotification *)inNotification 
{ 
    tap.enabled=false; //turn the gesture off so it wont consume the touch event 
} 

Lo que hace es deshabilitar el toque. Sin embargo, tuve que convertir el tap en una variable de instancia y liberarlo en dealloc.

4

Estaba mostrando una subvista desplegable que tenía su propia tabla. Como resultado, el touch.view a veces devuelve clases como UITableViewCell. Tenía que pasar a través de la superclase (s) para asegurarse de que era la subclase pensé que era:

-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch 
{ 
    UIView *view = touch.view; 
    while (view.class != UIView.class) { 
     // Check if superclass is of type dropdown 
     if (view.class == dropDown.class) { // dropDown is an ivar; replace with your own 
      NSLog(@"Is of type dropdown; returning NO"); 
      return NO; 
     } else { 
      view = view.superview; 
     } 
    } 

    return YES; 
} 
+0

El ciclo while representa esa – joslinm

2

Es posible prescindir de heredar ninguna clase.

se puede comprobar en gestureRecognizers selector de devolución de llamada del gesto

si view.gestureRecognizers no contiene su gestureRecognizer, simplemente lo ignoran

por ejemplo

- (void)viewDidLoad 
{ 
    UITapGestureRecognizer *singleTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self  action:@selector(handleSingleTap:)]; 
    singleTapGesture.numberOfTapsRequired = 1; 
} 

verificación Vista.gestureRecognizers aquí

- (void)handleSingleTap:(UIGestureRecognizer *)gestureRecognizer 
{ 
    UIEvent *event = [[UIEvent alloc] init]; 
    CGPoint location = [gestureRecognizer locationInView:self.view]; 

    //check actually view you hit via hitTest 
    UIView *view = [self.view hitTest:location withEvent:event]; 

    if ([view.gestureRecognizers containsObject:gestureRecognizer]) { 
     //your UIView 
     //do something 
    } 
    else { 
     //your UITableView or some thing else... 
     //ignore 
    } 
} 
+0

Como se menciona en otras respuestas, si utiliza este método, asegúrese de que el reconocedor de toques de toque reenvía la toma a su jerarquía de vista con: 'singleTapGesture.cancelsTouchesInView = NO;' agregado a 'viewDidLoad' arriba –

1

que crean una subclase UIGestureRecognizer diseñada para bloquear todos los reconocedores gesto unidas a un superviews de un punto de vista específico.

Es parte de mi proyecto WEPopover. Lo puedes encontrar here.

0

también hacía un popover y esto es cómo lo hice

func didTap(sender: UITapGestureRecognizer) { 

    let tapLocation = sender.locationInView(tableView) 

    if let _ = tableView.indexPathForRowAtPoint(tapLocation) { 
     sender.cancelsTouchesInView = false 
    } 
    else { 
     delegate?.menuDimissed() 
    } 
} 
0

implementar un delegado para todos los reconocedores del parentView y poner el método gestureRecognizer en el delegado que es responsable de la activación simultánea de los reconocedores :

func gestureRecognizer(UIGestureRecognizer,  shouldBeRequiredToFailByGestureRecognizer:UIGestureRecognizer) -> Bool { 
if (otherGestureRecognizer.view.isDescendantOfView(gestureRecognizer.view)) { 
    return true 
    } else { 
    return false 
} 

}

U puede utilizar el método falla si quieres hacer desencadenarse los niños, pero no es el padre reconocedores:

https://developer.apple.com/reference/uikit/uigesturerecognizerdelegate

1

Basándose en @Pin Shih Wang answer. Ignoramos todos los grifos que no sean los de la vista que contiene el reconocedor de gestos de toque. Todos los grifos se envían a la jerarquía de vista de forma normal, como hemos configurado tapGestureRecognizer.cancelsTouchesInView = false. Aquí está el código en Swift3/4:

func ensureBackgroundTapDismissesKeyboard() { 
    let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap)) 
    tapGestureRecognizer.cancelsTouchesInView = false 
    self.view.addGestureRecognizer(tapGestureRecognizer) 
} 

@objc func handleTap(recognizer: UIGestureRecognizer) { 
    let location = recognizer.location(in: self.view) 
    let hitTestView = self.view.hitTest(location, with: UIEvent()) 
    if hitTestView?.gestureRecognizers?.contains(recognizer) == .some(true) { 
     // I dismiss the keyboard on a tap on the scroll view 
     // REPLACE with own logic 
     self.view.endEditing(true) 
    } 
} 
Cuestiones relacionadas