recognizer ios iphone cocoa-touch uiscrollview uigesturerecognizer

ios - recognizer - Toques Adelantados a UIScrollView



tap gesture recognizer swift 4 (5)

Tengo dos controladores de vista. El controlador de vista A tiene un UIScrollView y presenta el controlador de vista B. La presentación es interactiva y está controlada por scrollView.contentOffset .

Quiero integrar una transición interactiva de descarte: cuando se realiza una panorámica, ViewController B debe descartarse interactivamente. La transición de desactivación interactiva también debe controlar ViewController A scrollView.

Mi primer intento fue usar un UIPanGestureRecognizer y configurar el scrollView.contentOffset acuerdo con la distancia recorrida. Esto funciona, sin embargo, cuando el gesto de panorámica termina, el desplazamiento de scrollView debe ser animado a la posición final. Usando -[UIScrollView setContentOffset:animated: no es una buena solución, ya que utiliza una animación lineal, no tiene en cuenta la velocidad actual del pan y no desacelera muy bien.

Así que pensé que debería ser posible alimentar los eventos táctiles desde mi reconocedor de gestos pan a la vista de desplazamiento. Esto debería darnos todo el buen comportamiento de animación de vista de desplazamiento.

Intenté sobreescribir los -touchesBegan/Moved/Ended/Cancelled withEvent: en mi subclase UIPanGestureRecognizer esta manera:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [scrollView touchesBegan:touches withEvent:event]; [scrollView.panGestureRecognizer touchesBegan:touches withEvent:event]; [super touchesBegan:touches withEvent:event]; }

Pero aparentemente algo está bloqueando la vista de desplazamiento de entrar en modo de tracking . (Va a dragging = YES pero eso es todo.) Comprobé que userInteractionEnabled es userInteractionEnabled , no está oculto y se ha agregado a la jerarquía de vistas.

Entonces, ¿cómo puedo reenviar mis eventos táctiles a UIScrollView ?


Tal vez puedas intentar usar el método en el delegado de UIPanGesture:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer;

No sé cómo reaccionará, ya que sus puntos de vista son modales. Pero tal vez podría tener éxito sin pasar el toque directamente con beginTouch, etc. que son un dolor en el culo.

Dime si ayuda?


Como una adición a la solución de @johnboiles. Cuando capte todo el rect, entonces se omitirán los elementos dentro de la vista de desplazamiento. Puede agregar áreas táctiles adicionales y para la vista de desplazamiento normal simplemente páselo a la prueba super.hit como esta:

class MagicScrollView: UIScrollView { override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? { if CGRectContainsPoint(CGRect(x: self.bounds.origin.x - 30, y: self.bounds.origin.y, width: 30, height: self.bounds.size.height), point) { return self } if CGRectContainsPoint(CGRect(x: self.bounds.origin.x + self.bounds.size.width, y: self.bounds.origin.y, width: 30, height: self.bounds.size.height), point) { return self } return super.hitTest(point, withEvent: event) } }


Tengo una vista de desplazamiento dentro de una supervista para tener bordes izquierdo y derecho. Para hacer esas fronteras desplazables, este era mi enfoque:

class CustomScrollView: UIScrollView { override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? { if let view = super.hitTest(point, withEvent: event) { return view } guard let superview = superview else { return nil } return superview.hitTest(superview.convertPoint(point, fromView: self), withEvent: event) } override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool { if super.pointInside(point, withEvent: event) { return true } guard let superview = superview else { return false } return superview.pointInside(superview.convertPoint(point, fromView: self), withEvent: event) } }

Funciona perfectamente


Después de leer una respuesta interesante que describía el flujo de eventos de UIScrollView , llegué a la conclusión de que tratar de "controlar a distancia" una vista de desplazamiento desde un reconocedor de gestos es probablemente muy difícil de lograr porque los toques se modifican mientras se enrutan a vistas y reconocedores de gestos. Como UITouch no se ajusta a NSCopying , tampoco podemos clonar eventos táctiles para enviarlos más tarde en estado no modificado.

Aunque no estoy realmente resolviendo el problema que pedí, encontré una solución para lograr lo que necesito. Acabo de agregar una vista de desplazamiento para ver el controlador B y lo sincronicé con la vista de desplazamiento del VC A (que se agrega a la jerarquía de la vista cuando se desplaza verticalmente):

// delegate of VC B''s scrollView - (void)scrollViewDidScroll:(UIScrollView*)scrollView scrollViewA.contentOffset = scrollView.contentOffset; }

Gracias a Friedrich Markgraf a quien se le ocurrió la idea .


Pruebe subclassing UIScrollView y reemplazando hitTest:withEvent: para que el UIScrollView toque toques fuera de sus límites. Luego obtienes todas las agradables animaciones UIScrollView gratis. Algo como esto:

@interface MagicScrollView : UIScrollView @end @implementation MagicScrollView - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { // Intercept touches 100pt outside this view''s bounds on all sides. Customize this for the touch area you want to intercept if (CGRectContainsPoint(CGRectInset(self.bounds, -100, -100), point)) { return self; } return nil; } @end

O en Swift (gracias Edwin Vermeer!)

class MagicScrollView: UIScrollView { override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? { if CGRectContainsPoint(CGRectInset(self.bounds, -100, -100), point) { return self } return nil } }

Es posible que también deba sobrescribir pointInside:withEvent: en la pointInside:withEvent: de UIScrollView , según su diseño.

Consulte la siguiente pregunta para obtener más información: interacción más allá de los límites de uiview