objective-c - framework - uikit ios
UIGestureRecognizer bloquea la subvista para manejar eventos táctiles (10)
Estoy tratando de descubrir cómo se hace de la manera correcta . Intenté representar la situación:
Estoy agregando un UITableView
como una subvista de un UIView
. El UIView
responde a un tap- y pinchGestureRecognizer
, pero al hacerlo, la tabla deja de reaccionar a esos dos gestos (todavía reacciona a los golpes).
Lo he hecho funcionar 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 la UIView
(la 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];
}
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 vistas de forma normal, como hemos establecido 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)
}
}
Creé una subclase UIGestureRecognizer diseñada para bloquear todos los reconocedores de gestos adjuntos a una supervista de una vista específica.
Es parte de mi proyecto WEPopover. Puedes encontrarlo here .
El bloqueo de eventos táctiles a subvistas es el comportamiento predeterminado. Usted puede cambiar este comportamiento:
UITapGestureRecognizer *r = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(agentPickerTapped:)];
r.cancelsTouchesInView = NO;
[agentPicker addGestureRecognizer:r];
Es posible prescindir de cualquier clase.
puedes verificar gestureRecognizers en el selector de devolución de llamada de gestos
si view.gestureRecognizers no contiene su gestureRecognizer, simplemente ignórelo
por ejemplo
- (void)viewDidLoad
{
UITapGestureRecognizer *singleTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTap:)];
singleTapGesture.numberOfTapsRequired = 1;
}
ver view.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
}
}
Estaba mostrando una subvista desplegable que tenía su propia tabla. Como resultado, touch.view
veces devolvería clases como UITableViewCell
. Tuve que pasar por la (s) superclase (s) para asegurarme de que era la subclase que 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;
}
Puedes apagarlo y encenderlo ... en mi código hice algo como esto porque necesitaba apagarlo cuando el teclado no se mostraba, puedes aplicarlo a tu situación:
llamar 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];
luego crea 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 esto hace es deshabilitar el toque. Sin embargo, tuve que convertir el tap en una variable de instancia y liberarlo en dealloc.
También estaba haciendo un popover y así es como lo hice
func didTap(sender: UITapGestureRecognizer) {
let tapLocation = sender.locationInView(tableView)
if let _ = tableView.indexPathForRowAtPoint(tapLocation) {
sender.cancelsTouchesInView = false
}
else {
delegate?.menuDimissed()
}
}
Tuve un problema muy similar y encontré mi solución en esta pregunta SO . En resumen, UIGestureRecognizer
como el delegado de 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
Una posibilidad es subclasificar su reconocedor de gestos (si no lo ha hecho) y anular -touchesBegan:withEvent:
modo que determine si cada toque comenzó en una subvista excluida y llama -ignoreTouch:forEvent:
para ese toque si lo hizo.
Obviamente, también deberá agregar una propiedad para realizar un seguimiento de la subvista excluida o, mejor aún, una matriz de subvistas excluidas.
implementar un delegado para todos los reconocedores de parentView y poner el método gestureRecognizer en el delegado que es responsable del disparo simultáneo de los reconocedores:
func gestureRecognizer(UIGestureRecognizer, shouldBeRequiredToFailByGestureRecognizer:UIGestureRecognizer) -> Bool {
if (otherGestureRecognizer.view.isDescendantOfView(gestureRecognizer.view)) {
return true
} else {
return false
}
}
U puede utilizar los métodos de falla si desea que los elementos secundarios se activen, pero no los reconocedores originales:
https://developer.apple.com/reference/uikit/uigesturerecognizerdelegate