¿Cómo puedo forzar un UIScrollView
en el que la paginación y el desplazamiento están activados para que solo se muevan vertical u horizontalmente en un momento dado?
Tengo entendido que la directionalLockEnabled
propiedad debería lograr esto, pero un deslizamiento diagonal aún hace que la vista se desplace en diagonal en lugar de restringir el movimiento a un solo eje.
Editar: para ser más claro, me gustaría permitir que el usuario se desplace horizontal O verticalmente, pero no ambos simultáneamente.
Respuestas:
Estás en una situación muy difícil, debo decir.
Tenga en cuenta que debe usar un UIScrollView con
pagingEnabled=YES
para cambiar entre páginas, pero debepagingEnabled=NO
desplazarse verticalmente.Hay 2 estrategias posibles. No sé cuál funcionará / es más fácil de implementar, así que pruebe ambos.
Primero: UIScrollViews anidados. Francamente, todavía tengo que ver a una persona que haya logrado que esto funcione. Sin embargo, personalmente no me he esforzado lo suficiente, y mi práctica demuestra que cuando lo intentas lo suficiente, puedes hacer que UIScrollView haga lo que quieras .
Entonces, la estrategia es dejar que la vista de desplazamiento exterior solo maneje el desplazamiento horizontal y las vistas de desplazamiento interno solo manejen el desplazamiento vertical. Para lograrlo, debe saber cómo funciona UIScrollView internamente. Anula el
hitTest
método y siempre se devuelve a sí mismo, de modo que todos los eventos táctiles van a UIScrollView. LuegotouchesBegan
, adentro ,touchesMoved
etc., verifica si está interesado en el evento y lo maneja o lo pasa a los componentes internos.Para decidir si el toque se debe manejar o reenviar, UIScrollView inicia un temporizador cuando lo toca por primera vez:
Si no ha movido el dedo significativamente en 150 ms, pasa el evento a la vista interior.
Si ha movido el dedo significativamente en 150 ms, comienza a desplazarse (y nunca pasa el evento a la vista interior).
Observe cómo cuando toca una tabla (que es una subclase de vista de desplazamiento) y comienza a desplazarse de inmediato, la fila que tocó nunca se resalta.
Si ha no movido el dedo significativamente dentro de 150ms y UIScrollView comenzó a pasar los eventos a la vista interno, pero luego ha movido el dedo lo suficiente para que el desplazamiento para empezar, UIScrollView llama
touchesCancelled
en la vista interior y empieza a oscilar.Tenga en cuenta que cuando toca una mesa, sostiene un poco el dedo y luego comienza a desplazarse, la fila que tocó se resalta primero, pero luego se quita el resaltado.
Esta secuencia de eventos se puede modificar mediante la configuración de UIScrollView:
delaysContentTouches
es NO, entonces no se usa ningún temporizador: los eventos pasan inmediatamente al control interno (pero luego se cancelan si mueve el dedo lo suficiente)cancelsTouches
es NO, una vez que los eventos se envían a un control, el desplazamiento nunca ocurrirá.Tenga en cuenta que es UIScrollView que recibe todo
touchesBegin
,touchesMoved
,touchesEnded
ytouchesCanceled
eventos de Cocoa Touch (debido a que suhitTest
dice que lo haga). Luego los reenvía a la vista interior si así lo desea, siempre que lo desee.Ahora que sabe todo sobre UIScrollView, puede modificar su comportamiento. Puedo apostar que quiere dar preferencia al desplazamiento vertical, de modo que una vez que el usuario toque la vista y comience a mover el dedo (aunque sea ligeramente), la vista comience a desplazarse en dirección vertical; pero cuando el usuario mueve su dedo en dirección horizontal lo suficiente, desea cancelar el desplazamiento vertical y comenzar el desplazamiento horizontal.
Desea subclasificar su UIScrollView externo (digamos, nombra su clase RemorsefulScrollView), de modo que en lugar del comportamiento predeterminado, reenvíe inmediatamente todos los eventos a la vista interna, y solo cuando se detecta un movimiento horizontal significativo, se desplaza.
¿Cómo hacer que RemorsefulScrollView se comporte de esa manera?
Parece que deshabilitar el desplazamiento vertical y establecer
delaysContentTouches
en NO debería hacer que UIScrollViews anidadas funcionen. Desafortunadamente, no es así; UIScrollView parece hacer un filtrado adicional para movimientos rápidos (que no se pueden deshabilitar), de modo que incluso si UIScrollView solo se puede desplazar horizontalmente, siempre consumirá (e ignorará) los movimientos verticales lo suficientemente rápidos.El efecto es tan severo que el desplazamiento vertical dentro de una vista de desplazamiento anidada es inutilizable. (Parece que tiene exactamente esta configuración, así que inténtelo: mantenga un dedo durante 150 ms y luego muévalo en dirección vertical; ¡UIScrollView anidado funciona como se esperaba entonces!)
Esto significa que no puede usar el código de UIScrollView para el manejo de eventos; debe anular los cuatro métodos de manejo táctil en RemorsefulScrollView y hacer su propio procesamiento primero, solo reenviando el evento a
super
(UIScrollView) si ha decidido ir con el desplazamiento horizontal.Sin embargo, debe pasar
touchesBegan
a UIScrollView, porque desea que recuerde una coordenada base para el desplazamiento horizontal futuro (si luego decide que es un desplazamiento horizontal). No podrá enviartouchesBegan
a UIScrollView más tarde, porque no puede almacenar eltouches
argumento: contiene objetos que serán mutados antes del próximotouchesMoved
evento y no puede reproducir el estado anterior.Por lo tanto, debe pasar
touchesBegan
a UIScrollView de inmediato, pero ocultará mástouchesMoved
eventos hasta que decida desplazarse horizontalmente. NotouchesMoved
significa que no haya desplazamiento, por lo que esta inicialtouchesBegan
no hará ningún daño. Pero configuredelaysContentTouches
en NO, para que no interfieran temporizadores sorpresa adicionales.(Offtopic: a diferencia de usted, UIScrollView puede almacenar toques correctamente y puede reproducir y reenviar el
touchesBegan
evento original más tarde. Tiene la ventaja injusta de usar API no publicadas, por lo que puede clonar objetos táctiles antes de que se muten).Dado que siempre reenvías
touchesBegan
, también tienes que reenviartouchesCancelled
ytouchesEnded
. Se debe girartouchesEnded
entouchesCancelled
, sin embargo, porque UIScrollView interpretaríatouchesBegan
,touchesEnded
secuencia como un toque del ratón, y le transmitirá a la vista interior. Ya está reenviando los eventos adecuados usted mismo, por lo que nunca desea que UIScrollView reenvíe nada.Básicamente, aquí hay un pseudocódigo para lo que necesita hacer. Para simplificar, nunca permito el desplazamiento horizontal después de que se haya producido un evento multitáctil.
// RemorsefulScrollView.h @interface RemorsefulScrollView : UIScrollView { CGPoint _originalPoint; BOOL _isHorizontalScroll, _isMultitouch; UIView *_currentChild; } @end // RemorsefulScrollView.m // the numbers from an example in Apple docs, may need to tune them #define kThresholdX 12.0f #define kThresholdY 4.0f @implementation RemorsefulScrollView - (id)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { self.delaysContentTouches = NO; } return self; } - (id)initWithCoder:(NSCoder *)coder { if (self = [super initWithCoder:coder]) { self.delaysContentTouches = NO; } return self; } - (UIView *)honestHitTest:(CGPoint)point withEvent:(UIEvent *)event { UIView *result = nil; for (UIView *child in self.subviews) if ([child pointInside:point withEvent:event]) if ((result = [child hitTest:point withEvent:event]) != nil) break; return result; } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [super touchesBegan:touches withEvent:event]; // always forward touchesBegan -- there's no way to forward it later if (_isHorizontalScroll) return; // UIScrollView is in charge now if ([touches count] == [[event touchesForView:self] count]) { // initial touch _originalPoint = [[touches anyObject] locationInView:self]; _currentChild = [self honestHitTest:_originalPoint withEvent:event]; _isMultitouch = NO; } _isMultitouch |= ([[event touchesForView:self] count] > 1); [_currentChild touchesBegan:touches withEvent:event]; } - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { if (!_isHorizontalScroll && !_isMultitouch) { CGPoint point = [[touches anyObject] locationInView:self]; if (fabsf(_originalPoint.x - point.x) > kThresholdX && fabsf(_originalPoint.y - point.y) < kThresholdY) { _isHorizontalScroll = YES; [_currentChild touchesCancelled:[event touchesForView:self] withEvent:event] } } if (_isHorizontalScroll) [super touchesMoved:touches withEvent:event]; // UIScrollView only kicks in on horizontal scroll else [_currentChild touchesMoved:touches withEvent:event]; } - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { if (_isHorizontalScroll) [super touchesEnded:touches withEvent:event]; else { [super touchesCancelled:touches withEvent:event]; [_currentChild touchesEnded:touches withEvent:event]; } } - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { [super touchesCancelled:touches withEvent:event]; if (!_isHorizontalScroll) [_currentChild touchesCancelled:touches withEvent:event]; } @end
No he intentado ejecutar o incluso compilar esto (y escribí toda la clase en un editor de texto sin formato), pero puede comenzar con lo anterior y, con suerte, hacerlo funcionar.
El único problema oculto que veo es que si agrega vistas secundarias que no sean UIScrollView a RemorsefulScrollView, los eventos táctiles que reenvía a un niño pueden llegar a usted a través de la cadena de respuesta, si el niño no siempre maneja los toques como lo hace UIScrollView. Una implementación de RemorsefulScrollView a prueba de balas protegería contra la
touchesXxx
reentrada.Segunda estrategia: si por alguna razón los UIScrollViews anidados no funcionan o resultan demasiado difíciles de hacerlo bien, puede intentar llevarse bien con un solo UIScrollView, cambiando su
pagingEnabled
propiedad sobre la marcha desde suscrollViewDidScroll
método delegado.Para evitar el desplazamiento diagonal, primero debe intentar recordar contentOffset adentro
scrollViewWillBeginDragging
y verificar y restablecer contentOffset adentroscrollViewDidScroll
si detecta un movimiento diagonal. Otra estrategia que puede probar es restablecer contentSize para permitir el desplazamiento en una sola dirección, una vez que decida en qué dirección se dirige el dedo del usuario. (UIScrollView parece bastante indulgente a la hora de jugar con contentSize y contentOffset desde sus métodos delegados).Si eso tampoco funciona o da como resultado imágenes descuidadas, debe anular
touchesBegan
,touchesMoved
etc. y no reenviar eventos de movimiento diagonal a UIScrollView. (Sin embargo, la experiencia del usuario no será óptima en este caso, ya que tendrá que ignorar los movimientos diagonales en lugar de forzarlos en una sola dirección. Si se siente realmente aventurero, puede escribir su propio UITouch similar, algo como RevengeTouch. Objetivo -C es simplemente C antiguo, y no hay nada más tipo pato en el mundo que C; siempre que nadie verifique la clase real de los objetos, lo que creo que nadie hace, puedes hacer que cualquier clase se vea como cualquier otra clase. Esto abre la posibilidad de sintetizar los toques que desee, con las coordenadas que desee).Estrategia de copia de seguridad: hay TTScrollView, una nueva implementación sensata de UIScrollView en la biblioteca Three20 . Desafortunadamente, el usuario lo siente muy poco natural y poco honesto. Pero si cada intento de usar UIScrollView falla, puede recurrir a una vista de desplazamiento con codificación personalizada. Recomiendo no hacerlo si es posible; El uso de UIScrollView garantiza que obtenga el aspecto y la sensación nativos, sin importar cómo evolucione en las futuras versiones del iPhone OS.
De acuerdo, este pequeño ensayo se ha vuelto un poco largo. Todavía estoy en los juegos de UIScrollView después del trabajo en ScrollingMadness hace varios días.
PD: Si alguno de estos funciona y tiene ganas de compartirlo, envíeme un correo electrónico con el código correspondiente a [email protected]. Con mucho gusto lo incluiría en mi bolsa de trucos ScrollingMadness .
PPS Añadiendo este pequeño ensayo a ScrollingMadness README.
fuente
Esto funciona para mí cada vez ...
scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * NumberOfPages, 1);
fuente
Para la gente perezosa como yo:
Establecer
scrollview.directionalLockEnabled
enYES
- (void) scrollViewWillBeginDragging: (UIScrollView *) scrollView { self.oldContentOffset = scrollView.contentOffset; } - (void) scrollViewDidScroll: (UIScrollView *) scrollView { if (scrollView.contentOffset.x != self.oldContentOffset.x) { scrollView.pagingEnabled = YES; scrollView.contentOffset = CGPointMake(scrollView.contentOffset.x, self.oldContentOffset.y); } else { scrollView.pagingEnabled = NO; } } - (void) scrollViewDidEndDecelerating: (UIScrollView *) scrollView { self.oldContentOffset = scrollView.contentOffset; }
fuente
Utilicé el siguiente método para resolver este problema, espero que te ayude.
Primero defina las siguientes variables en el archivo de encabezado de sus controladores.
CGPoint startPos; int scrollDirection;
startPos mantendrá el valor contentOffset cuando su delegado reciba el mensaje scrollViewWillBeginDragging . Entonces en este método hacemos esto;
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{ startPos = scrollView.contentOffset; scrollDirection=0; }
luego usamos estos valores para determinar la dirección de desplazamiento deseada por los usuarios en el mensaje scrollViewDidScroll .
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{ if (scrollDirection==0){//we need to determine direction //use the difference between positions to determine the direction. if (abs(startPos.x-scrollView.contentOffset.x)<abs(startPos.y-scrollView.contentOffset.y)){ NSLog(@"Vertical Scrolling"); scrollDirection=1; } else { NSLog(@"Horitonzal Scrolling"); scrollDirection=2; } } //Update scroll position of the scrollview according to detected direction. if (scrollDirection==1) { [scrollView setContentOffset:CGPointMake(startPos.x,scrollView.contentOffset.y) animated:NO]; } else if (scrollDirection==2){ [scrollView setContentOffset:CGPointMake(scrollView.contentOffset.x,startPos.y) animated:NO]; } }
finalmente tenemos que detener todas las operaciones de actualización cuando el usuario termina de arrastrar;
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{ if (decelerate) { scrollDirection=3; } }
fuente
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{ if (decelerate) { scrollDirection=3; } }
se cambia a- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{ if (!decelerate) { scrollDirection=0; } }
y- (void)scrollViewDidEndDecelerating{ scrollDirection=0; }
Puede que esté excavando aquí, pero me encontré con esta publicación hoy porque estaba tratando de resolver el mismo problema. Aquí está mi solución, parece estar funcionando muy bien.
Quiero mostrar una pantalla llena de contenido, pero permitir que el usuario se desplace a una de las otras 4 pantallas completas, arriba, abajo, izquierda y derecha. Tengo un único UIScrollView con un tamaño de 320x480 y un contentSize de 960x1440. El desplazamiento de contenido comienza en 320,480. En scrollViewDidEndDecelerating :, redibujo las 5 vistas (vistas centrales y las 4 a su alrededor) y restablezco el desplazamiento de contenido a 320,480.
Aquí está el meollo de mi solución, el método scrollViewDidScroll :.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView { if (!scrollingVertically && ! scrollingHorizontally) { int xDiff = abs(scrollView.contentOffset.x - 320); int yDiff = abs(scrollView.contentOffset.y - 480); if (xDiff > yDiff) { scrollingHorizontally = YES; } else if (xDiff < yDiff) { scrollingVertically = YES; } } if (scrollingHorizontally) { scrollView.contentOffset = CGPointMake(scrollView.contentOffset.x, 480); } else if (scrollingVertically) { scrollView.contentOffset = CGPointMake(320, scrollView.contentOffset.y); } }
fuente
Chicos, es tan simple como hacer que la altura de la subvista sea la misma que la altura de la vista de desplazamiento y la altura del tamaño del contenido. Entonces el desplazamiento vertical está desactivado.
fuente
Aquí está mi implementación de un paginado
UIScrollView
que solo permite pergaminos ortogonales. Asegúrese de establecerlopagingEnabled
enYES
.PagedOrthoScrollView.h
@interface PagedOrthoScrollView : UIScrollView @end
PagedOrthoScrollView.m
#import "PagedOrthoScrollView.h" typedef enum { PagedOrthoScrollViewLockDirectionNone, PagedOrthoScrollViewLockDirectionVertical, PagedOrthoScrollViewLockDirectionHorizontal } PagedOrthoScrollViewLockDirection; @interface PagedOrthoScrollView () @property(nonatomic, assign) PagedOrthoScrollViewLockDirection dirLock; @property(nonatomic, assign) CGFloat valueLock; @end @implementation PagedOrthoScrollView @synthesize dirLock; @synthesize valueLock; - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self == nil) { return self; } self.dirLock = PagedOrthoScrollViewLockDirectionNone; self.valueLock = 0; return self; } - (void)setBounds:(CGRect)bounds { int mx, my; if (self.dirLock == PagedOrthoScrollViewLockDirectionNone) { // is on even page coordinates, set dir lock and lock value to closest page mx = abs((int)CGRectGetMinX(bounds) % (int)CGRectGetWidth(self.bounds)); if (mx != 0) { self.dirLock = PagedOrthoScrollViewLockDirectionHorizontal; self.valueLock = (round(CGRectGetMinY(bounds) / CGRectGetHeight(self.bounds)) * CGRectGetHeight(self.bounds)); } else { self.dirLock = PagedOrthoScrollViewLockDirectionVertical; self.valueLock = (round(CGRectGetMinX(bounds) / CGRectGetWidth(self.bounds)) * CGRectGetWidth(self.bounds)); } // show only possible scroll indicator self.showsVerticalScrollIndicator = dirLock == PagedOrthoScrollViewLockDirectionVertical; self.showsHorizontalScrollIndicator = dirLock == PagedOrthoScrollViewLockDirectionHorizontal; } if (self.dirLock == PagedOrthoScrollViewLockDirectionHorizontal) { bounds.origin.y = self.valueLock; } else { bounds.origin.x = self.valueLock; } mx = abs((int)CGRectGetMinX(bounds) % (int)CGRectGetWidth(self.bounds)); my = abs((int)CGRectGetMinY(bounds) % (int)CGRectGetHeight(self.bounds)); if (mx == 0 && my == 0) { // is on even page coordinates, reset lock self.dirLock = PagedOrthoScrollViewLockDirectionNone; } [super setBounds:bounds]; } @end
fuente
También tuve que resolver este problema, y aunque la respuesta de Andrey Tarantsov definitivamente tiene mucha información útil para comprender cómo
UIScrollViews
funciona, creo que la solución es un poco complicada. Mi solución, que no implica ninguna subclase o reenvío táctil, es la siguiente:UIPanGestureRecognizers
, nuevamente uno para cada dirección.UIScrollViews
propiedades 'panGestureRecognizer y el maniquíUIPanGestureRecognizer
correspondiente a la dirección perpendicular a la dirección de desplazamiento deseada de su UIScrollView usando elUIGestureRecognizer's
-requireGestureRecognizerToFail:
selector.-gestureRecognizerShouldBegin:
devoluciones de llamada para sus UIPanGestureRecognizers ficticios, calcule la dirección inicial de la panorámica utilizando el selector -translationInView: del gesto (UIPanGestureRecognizers
no llamará a esta devolución de llamada delegada hasta que su toque se haya traducido lo suficiente para registrarse como una panorámica). Permita o no permita que su reconocedor de gestos comience dependiendo de la dirección calculada, que a su vez debería controlar si el reconocedor de gestos de panorámica UIScrollView perpendicular podrá o no comenzar.-gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:
, no permita que su muñeco seUIPanGestureRecognizers
ejecute junto con el desplazamiento (a menos que tenga algún comportamiento adicional que desee agregar).fuente
Lo que hice fue crear un UIScrollView de paginación con un marco del tamaño de la pantalla de visualización y establecer el tamaño del contenido al ancho necesario para todo mi contenido y una altura de 1 (gracias Tonetel). Luego creé las páginas del menú UIScrollView con marcos configurados para cada página, el tamaño de la pantalla de visualización y configuré el tamaño del contenido de cada una al ancho de la pantalla y la altura necesaria para cada una. Luego, simplemente agregué cada una de las páginas del menú a la vista de paginación. Asegúrese de que la paginación esté habilitada en la vista de desplazamiento de paginación pero deshabilitada en las vistas del menú (debería estar deshabilitada de forma predeterminada) y debería estar listo para comenzar.
La vista de desplazamiento de paginación ahora se desplaza horizontalmente, pero no verticalmente debido a la altura del contenido. Cada página se desplaza verticalmente pero no horizontalmente debido a sus limitaciones de tamaño de contenido también.
Aquí está el código si esa explicación dejó algo que desear:
UIScrollView *newPagingScrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds]; [newPagingScrollView setPagingEnabled:YES]; [newPagingScrollView setShowsVerticalScrollIndicator:NO]; [newPagingScrollView setShowsHorizontalScrollIndicator:NO]; [newPagingScrollView setDelegate:self]; [newPagingScrollView setContentSize:CGSizeMake(self.view.bounds.size.width * NumberOfDetailPages, 1)]; [self.view addSubview:newPagingScrollView]; float pageX = 0; for (int i = 0; i < NumberOfDetailPages; i++) { CGRect pageFrame = (CGRect) { .origin = CGPointMake(pageX, pagingScrollView.bounds.origin.y), .size = pagingScrollView.bounds.size }; UIScrollView *newPage = [self createNewPageFromIndex:i ToPageFrame:pageFrame]; // newPage.contentSize custom set in here [pagingScrollView addSubview:newPage]; pageX += pageFrame.size.width; }
fuente
Gracias Tonetel. Modifiqué ligeramente su enfoque, pero era exactamente lo que necesitaba para evitar el desplazamiento horizontal.
self.scrollView.contentSize = CGSizeMake(self.scrollView.contentSize.width, 1);
fuente
Esta es una solución mejorada de icompot, que era bastante inestable para mí. Este funciona bien y es fácil de implementar:
- (void) scrollViewWillBeginDragging: (UIScrollView *) scrollView { self.oldContentOffset = scrollView.contentOffset; } - (void) scrollViewDidScroll: (UIScrollView *) scrollView { float XOffset = fabsf(self.oldContentOffset.x - scrollView.contentOffset.x ); float YOffset = fabsf(self.oldContentOffset.y - scrollView.contentOffset.y ); if (scrollView.contentOffset.x != self.oldContentOffset.x && (XOffset >= YOffset) ) { scrollView.pagingEnabled = YES; scrollView.contentOffset = CGPointMake(scrollView.contentOffset.x, self.oldContentOffset.y); } else { scrollView.pagingEnabled = NO; scrollView.contentOffset = CGPointMake( self.oldContentOffset.x, scrollView.contentOffset.y); } } - (void) scrollViewDidEndDecelerating: (UIScrollView *) scrollView { self.oldContentOffset = scrollView.contentOffset; }
fuente
Para referencia futura, me basé en la respuesta de Andrey Tanasov y se me ocurrió la siguiente solución que funciona bien.
Primero defina una variable para almacenar el contentOffset y configúrelo en coordenadas 0,0
var positionScroll = CGPointMake(0, 0)
Ahora implemente lo siguiente en el delegado scrollView:
override func scrollViewWillBeginDragging(scrollView: UIScrollView) { // Note that we are getting a reference to self.scrollView and not the scrollView referenced passed by the method // For some reason, not doing this fails to get the logic to work self.positionScroll = (self.scrollView?.contentOffset)! } override func scrollViewDidScroll(scrollView: UIScrollView) { // Check that contentOffset is not nil // We do this because the scrollViewDidScroll method is called on viewDidLoad if self.scrollView?.contentOffset != nil { // The user wants to scroll on the X axis if self.scrollView?.contentOffset.x > self.positionScroll.x || self.scrollView?.contentOffset.x < self.positionScroll.x { // Reset the Y position of the scrollView to what it was BEFORE scrolling started self.scrollView?.contentOffset = CGPointMake((self.scrollView?.contentOffset.x)!, self.positionScroll.y); self.scrollView?.pagingEnabled = true } // The user wants to scroll on the Y axis else { // Reset the X position of the scrollView to what it was BEFORE scrolling started self.scrollView?.contentOffset = CGPointMake(self.positionScroll.x, (self.scrollView?.contentOffset.y)!); self.collectionView?.pagingEnabled = false } } }
Espero que esto ayude.
fuente
De los documentos:
"el valor predeterminado es NO, lo que significa que se permite el desplazamiento tanto en dirección horizontal como vertical. Si el valor es SÍ y el usuario comienza a arrastrar en una dirección general (horizontal o verticalmente), la vista de desplazamiento deshabilita el desplazamiento en la otra dirección. "
Creo que la parte importante es "si ... el usuario comienza a arrastrar en una dirección general". Entonces, si comienzan a arrastrar en diagonal, esto no se activa. No es que estos documentos sean siempre confiables en lo que respecta a la interpretación, pero eso parece encajar con lo que está viendo.
Esto realmente parece sensato. Tengo que preguntar: ¿por qué quiere restringirse solo a horizontal o solo vertical en cualquier momento? ¿Quizás UIScrollView no es la herramienta para usted?
fuente
Mi vista consta de 10 vistas horizontales, y algunas de esas vistas consisten en múltiples vistas verticales, por lo que obtendrá algo como esto:
1.0 2.0 3.1 4.1 2.1 3.1 2.2 2.3
Con la paginación habilitada tanto en el eje horizontal como en el vertical
La vista también tiene los siguientes atributos:
[self setDelaysContentTouches:NO]; [self setMultipleTouchEnabled:NO]; [self setDirectionalLockEnabled:YES];
Ahora, para evitar el desplazamiento diagonal, haga esto:
-(void)scrollViewDidScroll:(UIScrollView *)scrollView { int pageWidth = 768; int pageHeight = 1024; int subPage =round(self.contentOffset.y / pageHeight); if ((int)self.contentOffset.x % pageWidth != 0 && (int)self.contentOffset.y % pageHeight != 0) { [self setContentOffset:CGPointMake(self.contentOffset.x, subPage * pageHeight]; } }
¡Funciona como un encanto para mí!
fuente
Necesita restablecer
_isHorizontalScroll
a NO entouchesEnded
ytouchesCancelled
.fuente
Si tiene una vista de desplazamiento grande ... y desea restringir el desplazamiento diagonal http://chandanshetty01.blogspot.in/2012/07/restricting-diagonal-scrolling-in.html
fuente
Si no ha intentado hacer esto en la versión 3.0 beta, le recomiendo que lo pruebe. Actualmente estoy usando una serie de vistas de tabla dentro de una vista de desplazamiento y funciona como se esperaba. (Bueno, el desplazamiento lo hace, de todos modos. Encontré esta pregunta cuando buscaba una solución a un problema totalmente diferente ...)
fuente
Para aquellos que buscan en Swift en @AndreyTarantsov, la segunda solución es así en el código:
var positionScroll:CGFloat = 0 func scrollViewWillBeginDragging(scrollView: UIScrollView) { positionScroll = self.theScroll.contentOffset.x } func scrollViewDidScroll(scrollView: UIScrollView) { if self.theScroll.contentOffset.x > self.positionScroll || self.theScroll.contentOffset.x < self.positionScroll{ self.theScroll.pagingEnabled = true }else{ self.theScroll.pagingEnabled = false } }
lo que estamos haciendo aquí es mantener la posición actual justo antes de que se arrastre scrollview y luego verificar si x ha aumentado o disminuido en comparación con la posición actual de x. Si es así, establezca pagingEnabled en verdadero, si no (y ha aumentado / disminuido), establezca pagingEnabled en falso
¡Espero que sea útil para los recién llegados!
fuente
Logré resolver esto implementando scrollViewDidBeginDragging (_ :) y mirando la velocidad en el UIPanGestureRecognizer subyacente.
NB: Con esta solución, scrollView ignorará cada pan que hubiera sido diagonal.
override func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { super.scrollViewWillBeginDragging(scrollView) let velocity = scrollView.panGestureRecognizer.velocity(in: scrollView) if velocity.x != 0 && velocity.y != 0 { scrollView.panGestureRecognizer.isEnabled = false scrollView.panGestureRecognizer.isEnabled = true } }
fuente
Si está utilizando el generador de interfaces para diseñar su interfaz, esto se puede hacer con bastante facilidad.
En el generador de interfaces, simplemente haga clic en UIScrollView y seleccione la opción (en el inspector) que lo limita a un solo desplazamiento en una dirección.
fuente