ios uiscrollview uigesturerecognizer touches

ios - Permitir que UIScrollView y sus subvistas respondan a un toque



uigesturerecognizer touches (5)

Estaba teniendo el mismo problema, pero con una vista de desplazamiento que estaba dentro de UIPageViewController , por lo que tenía que ser manejado de manera ligeramente diferente.

Al cambiar la propiedad cancelsTouchesInView a falso para cada reconocedor en el UIScrollView , pude recibir toques en los botones dentro del UIPageViewController .

Lo hice agregando este código en viewDidLoad :

guard let recognizers = self.pageViewController.view.subviews[0].gestureRecognizers else { print("No gesture recognizers on scrollview.") return } for recognizer in recognizers { recognizer.cancelsTouchesInView = false }

Quiero que tanto mi UIScrollView como sus subvistas reciban todos los eventos táctiles dentro de la subvista. Cada uno puede responder a su manera.

Alternativamente, si los gestos de tap se enviaran a subvistas, todo estaría bien.

Mucha gente está luchando en esta área general. Aquí están algunas de las muchas preguntas relacionadas:

¿Cómo UIScrollView roba toques de sus subvistas?
¿Cómo robar toques de UIScrollView?
Cómo cancelar el desplazamiento en UIScrollView

Por cierto, si anulo hitTest: withEvent: en la vista de desplazamiento, veo los toques siempre que userInteractionEnabled sea YES. Pero eso realmente no resuelve mi problema, porque:

1) En ese momento, no sé si es un toque o no.
2) A veces necesito establecer userInteractionEnabled en NO.

EDITAR: Para aclarar, sí, quiero tratar los grifos de forma diferente a las bandejas. Los grifos deben ser manejados por subvistas. Las panorámicas se pueden manejar mediante la vista de desplazamiento de la forma habitual.


No sé si esto te puede ayudar, pero tuve un problema similar, en el que quería que la vista de desplazamiento manejara el doble toque, pero un solo toque hacia adelante para subvistas. Aquí está el código utilizado en un CustomScrollView

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { UITouch* touch = [touches anyObject]; // Coordinates CGPoint point = [touch locationInView:[self.subviews objectAtIndex:0]]; // One tap, forward if(touch.tapCount == 1){ // for each subview for(UIView* overlayView in self.subviews){ // Forward to my subclasss only if([overlayView isKindOfClass:[OverlayView class]]){ // translate coordinate CGPoint newPoint = [touch locationInView:overlayView]; //NSLog(@"%@",NSStringFromCGPoint(newPoint)); BOOL isInside = [overlayView pointInside:newPoint withEvent:event]; //if subview is hit if(isInside){ Forwarding [overlayView touchesEnded:touches withEvent:event]; break; } } } } // double tap : handle zoom else if(touch.tapCount == 2){ if(self.zoomScale == self.maximumZoomScale){ [self setZoomScale:[self minimumZoomScale] animated:YES]; } else { CGRect zoomRect = [self zoomRectForScrollView:self withScale:self.maximumZoomScale withCenter:point]; [self zoomToRect:zoomRect animated:YES]; } [self setNeedsDisplay]; } }

Por supuesto, el código efectivo debe cambiarse, pero en este punto debe tener toda la información que necesita para decidir si debe reenviar el evento. Puede que necesite implementar esto en otro método como touchesMoved: withEvent :.

Espero que esto pueda ayudar.


Si lo que necesita es diferir entre un toque y un desplazamiento, entonces puede probar si los toques se han movido. Si se trata de un toque, no se llamará a touchHasBeenMoved, entonces puede suponer que se trata de un toque.

En este punto, puede configurar un valor booleano para indicar si un movimiento se desarrolló y establecer este booleano como condición en sus otros métodos.

Estoy en la carretera, pero si eso es lo que necesita, podré explicarlo mejor más adelante.


Una forma pirata de lograr su objetivo, no del 100% exacto, es subclasificar la ventana de UI y anular el evento - (void) sendEvent: (UIEvent *);

Un ejemplo rápido:

en el encabezado de SecondResponderWindow.h

//SecondResponderWindow.h @protocol SecondResponderWindowDelegate - (void)userTouchBegan:(id)tapPoint onView:(UIView*)aView; - (void)userTouchMoved:(id)tapPoint onView:(UIView*)aView; - (void)userTouchEnded:(id)tapPoint onView:(UIView*)aView; @end @interface SecondResponderWindow : UIWindow @property (nonatomic, retain) UIView *viewToObserve; @property (nonatomic, assign) id <SecondResponderWindowDelegate> controllerThatObserves; @end

en SecondResponderWindow.m

//SecondResponderWindow.m - (void)forwardTouchBegan:(id)touch onView:(UIView*)aView { [controllerThatObserves userTouchBegan:touch onView:aView]; } - (void)forwardTouchMoved:(id)touch onView:(UIView*)aView { [controllerThatObserves userTouchMoved:touch onView:aView]; } - (void)forwardTouchEnded:(id)touch onView:(UIView*)aView { [controllerThatObserves userTouchEnded:touch onView:aView]; } - (void)sendEvent:(UIEvent *)event { [super sendEvent:event]; if (viewToObserve == nil || controllerThatObserves == nil) return; NSSet *touches = [event allTouches]; UITouch *touch = [touches anyObject]; if ([touch.view isDescendantOfView:viewToObserve] == NO) return; CGPoint tapPoint = [touch locationInView:viewToObserve]; NSValue *pointValue = [NSValue valueWithCGPoint:tapPoint]; if (touch.phase == UITouchPhaseBegan) [self forwardTouchBegan:pointValue onView:touch.view]; else if (touch.phase == UITouchPhaseMoved) [self forwardTouchMoved:pointValue onView:touch.view]; else if (touch.phase == UITouchPhaseEnded) [self forwardTouchEnded:pointValue onView:touch.view]; else if (touch.phase == UITouchPhaseCancelled) [self forwardTouchEnded:pointValue onView:touch.view]; }

No se ajusta al 100% a lo que estaba esperando, ya que la vista del segundo respondedor no controla el evento táctil de forma nativa a través de -touchDidBegin: más o menos, y tiene que implementar el SecondResponderWindowDelegate. Sin embargo, este truco le permite manejar eventos táctiles en respondedores adicionales.

Este método está inspirado y extendido desde TapDetectingWindow de MITHIN KUMAR


Primero, un descargo de responsabilidad. Si configura userInteractionEnabled en NO en UIScrollView , no se pasarán eventos táctiles a las subvistas. Por lo que yo sé, no hay forma de evitarlo con una excepción: interceptar eventos táctiles en la vista de supervisión de UIScrollView , y específicamente pasar esos eventos a las subvistas de UIScrollView . Sin embargo, para ser honesto, no sé por qué querrías hacer esto. Si desea deshabilitar la funcionalidad específica de UIScrollView (como ... bueno, desplazamiento) puede hacerlo fácilmente sin deshabilitar UserInteraction.

Si entiendo su pregunta, ¿necesita eventos de tap para ser procesados ​​por UIScrollView y pasados ​​a las subvistas? En cualquier caso (cualquiera que sea el gesto), creo que lo que está buscando es el método de protocolo gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer: con el UIGestureRecognizerDelegate gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer: en el protocolo UIGestureRecognizerDelegate . En sus subvistas, independientemente de los reconocedores de gestos que tenga, establezca un delegado (probablemente de cualquier clase que esté configurando el UIGestureReconginzer en primer lugar) en el reconocedor de gestos. Anule el método anterior y devuelva YES . Ahora, este gesto se reconocerá junto con cualquier otro reconocedor que pueda haber "robado" el gesto (en su caso, un toque). Con este método, incluso puede ajustar su código para enviar solo ciertos tipos de gestos a las subvistas o enviar el gesto solo en ciertas situaciones. Te da mucho control. Solo asegúrese de leer sobre el método, especialmente esta parte:

Se llama a este método cuando el reconocimiento de un gesto por gestos de reconocimiento u otro gesto de reconocimiento de bloque bloquea el reconocedor de gestos de reconocer su gesto. Tenga en cuenta que devolver SÍ está garantizado para permitir el reconocimiento simultáneo; Por otro lado, no se garantiza que el NO devuelva el reconocimiento simultáneo porque el delegado del otro reconocedor de gestos puede devolver SÍ.

Por supuesto, hay una advertencia: esto solo se aplica a los reconocedores de gestos. Por lo tanto, es posible que aún tengas problemas si intentas usar touchesBegan: touchesEnded , etc. para procesar los toques. Por supuesto, puede usar hitTest: para enviar eventos táctiles en bruto a las subvistas, pero ¿por qué? ¿Por qué procesar los eventos utilizando esos métodos en UIView , cuando puede adjuntar un UIGestureRecognizer a una vista y obtener toda esa funcionalidad de forma gratuita? Si necesita que los toques se procesen de una manera que ningún UIGestureRecognizer estándar pueda proporcionar, subclase UIGestureRecognizer y procese los toques allí. De esa manera, obtendrá toda la funcionalidad de un UIGestureRecognizer junto con su propio procesamiento táctil personalizado. Realmente creo que Apple pretendía que UIGestureRecognizer reemplazara la mayoría (si no todos) del código de procesamiento táctil personalizado que los desarrolladores usan en UIView . Permite la reutilización del código y es mucho más fácil de manejar cuando se mitiga qué código procesa qué evento táctil.