UIScrollView: pagination horizontale, défilement vertical?

Comment puis-je forcer un UIScrollView dans lequel la pagination et le défilement sont activés pour ne bouger que verticalement ou horizontalement à un moment donné?

Je crois savoir que la propriété directionalLockEnabled doit y parvenir, mais qu’un balayage diagonal fait toujours défiler la vue en diagonale au lieu de limiter le mouvement à un seul axe.

Edit: pour être plus clair, je voudrais permettre à l’utilisateur de faire défiler horizontalement OU verticalement, mais pas les deux simultanément.

Vous êtes dans une situation très difficile, je dois le dire.

Notez que vous devez utiliser un UIScrollView avec pagingEnabled=YES pour basculer entre les pages, mais vous avez besoin de pagingEnabled=NO pour faire défiler verticalement.

Il y a 2 stratégies possibles. Je ne sais pas lequel fonctionnera / est plus facile à mettre en œuvre, alors essayez les deux.

Tout d’abord: UIScrollViews nesteds. Franchement, je ne vois pas encore une personne qui a ce travail. Cependant, je n’ai pas assez essayé personnellement, et ma pratique montre que lorsque vous essayez suffisamment, vous pouvez faire en sorte que UIScrollView fasse tout ce que vous voulez .

La stratégie consiste donc à laisser la vue de défilement externe uniquement gérer le défilement horizontal et les vues de défilement internes à gérer uniquement le défilement vertical. Pour ce faire, vous devez savoir comment UIScrollView fonctionne en interne. Il remplace la méthode hitTest et retourne toujours lui-même, de sorte que tous les événements tactiles entrent dans UIScrollView. Ensuite, à l’intérieur de touchesBegan , touchesMoved etc., il vérifie s’il est intéressé par l’événement et le gère ou le transmet aux composants internes.

Pour décider si le toucher doit être manipulé ou transféré, UIScrollView lance un minuteur lorsque vous le touchez pour la première fois:

  • Si vous n’avez pas déplacé votre doigt de manière significative à moins de 150 ms, il transmet l’événement à la vue interne.

  • Si vous avez déplacé votre doigt de manière significative dans les 150 ms, il commence à défiler (et ne transmet jamais l’événement à la vue interne).

    Notez que lorsque vous touchez une table (qui est une sous-classe de défilement) et que vous commencez à défiler immédiatement, la ligne que vous avez touchée n’est jamais mise en surbrillance.

  • Si vous n’avez pas déplacé votre doigt de manière significative dans les 150 ms et que UIScrollView a commencé à transmettre les événements à la vue interne, alors vous avez déplacé le doigt suffisamment pour que le défilement commence, UIScrollView appelle touchesCancelled sur la vue interne et commence à défiler.

    Notez que lorsque vous touchez une table, maintenez un doigt sur votre doigt puis commencez à défiler, la ligne que vous avez touchée est mise en surbrillance en premier, mais décochée ensuite.

Ces séquences d’événements peuvent être modifiées par la configuration de UIScrollView:

  • Si delaysContentTouches est NON, alors aucun temporisateur n’est utilisé – les événements vont immédiatement au contrôle interne (mais sont ensuite annulés si vous déplacez votre doigt assez loin)
  • Si cancelsTouches est NON, une fois que les événements sont envoyés à un contrôle, le défilement ne se produira jamais.

Notez que c’est UIScrollView qui reçoit toutes les touchesBegin . touchesBegin , touchesMoved , touchesEnded et touchesEnded events from CocoaTouch (parce que son hitTest dit de le faire). Il les transmet ensuite à la vue intérieure si elle le souhaite, aussi longtemps qu’elle le souhaite.

Maintenant que vous savez tout sur UIScrollView, vous pouvez modifier son comportement. Je peux parier que vous voulez donner la préférence au défilement vertical, de sorte qu’une fois que l’utilisateur touche la vue et commence à bouger son doigt (même légèrement), la vue commence à défiler dans la direction verticale; mais lorsque l’utilisateur déplace son doigt dans la direction horizontale suffisamment loin, vous souhaitez annuler le défilement vertical et lancer le défilement horizontal.

Vous voulez sous-classer votre UIScrollView externe (par exemple, vous nommez votre classe RemorsefulScrollView), de sorte qu’au lieu du comportement par défaut, il transfère immédiatement tous les événements à la vue interne et uniquement lorsqu’un mouvement horizontal significatif est détecté.

Comment faire pour que RemorsefulScrollView se comporte de cette façon?

  • Il semble que la désactivation du défilement vertical et la définition de delaysContentTouches sur NO doivent rendre nestedes UIScrollViews. Malheureusement, ce n’est pas le cas. UIScrollView semble effectuer un filtrage supplémentaire pour les mouvements rapides (qui ne peuvent pas être désactivés), de sorte que même si UIScrollView ne peut être défilé que horizontalement, il consum toujours (et ignore) des mouvements verticaux suffisamment rapides.

    L’effet est si grave que le défilement vertical à l’intérieur d’une vue de défilement nestede est inutilisable. (Il semble que vous ayez exactement cette configuration, alors essayez-le: maintenez un doigt pendant 150 ms, puis déplacez-le dans le sens vertical – UIScrollView nested fonctionne comme prévu alors!)

  • Cela signifie que vous ne pouvez pas utiliser le code UIScrollView pour la gestion des événements; Vous devez remplacer toutes les quatre méthodes de gestion tactile dans RemorsefulScrollView et effectuer votre propre traitement en premier, en transférant uniquement l’événement à super (UIScrollView) si vous avez décidé d’utiliser le défilement horizontal.

  • Cependant, vous devez transmettre touchesBegan à UIScrollView, car vous voulez qu’il se souvienne d’une coordonnée de base pour le défilement horizontal futur (si vous décidez plus tard qu’il s’agit d’ un défilement horizontal). touchesBegan tard, vous ne pourrez pas envoyer de touchesBegan à UIScrollView, car vous ne pouvez pas stocker l’argument touches : il contient des objects qui seront mutés avant l’événement touchesMoved suivant et vous ne pouvez pas reproduire l’ancien état.

    Donc, vous devez passer touchesBegan à UIScrollView immédiatement, mais vous en touchesMoved tout autre événement jusqu’à ce que vous décidiez de faire défiler horizontalement. Sans touchesMoved signifie pas de défilement, donc cette touchesBegan initialeBegan ne fera aucun mal. Mais réglez delaysContentTouches sur NO, de sorte qu’aucun autre temporisateur de surprise n’interfère.

    (Offtopic – contrairement à vous, UIScrollView peut stocker les contacts correctement et peut reproduire et transférer l’événement touchesBegan original plus tard. Il présente un avantage indu d’utiliser des API non publiées, donc peut cloner des objects tactiles avant qu’ils ne soient mutés.)

  • Étant donné que vous touchesBegan toujours des touchesBegan , vous devez également avancer des touchesEnded et touchesEnded . Vous devez tourner les touchesEnded en touchesEnded , car UIScrollView interpréterait touchesBegan , touchesEnded sequence comme un clic, et le transmettrait à la vue interne. Vous transférez déjà les événements appropriés vous-même, alors vous ne voulez jamais que UIScrollView transmette quoi que ce soit.

Fondamentalement, voici le pseudocode pour ce que vous devez faire. Pour simplifier, je ne permets jamais le défilement horizontal après que l’événement multitouch soit survenu.

 // 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 

Je n'ai pas essayé de courir ou même de comstackr ceci (et de taper la classe entière dans un éditeur de texte brut), mais vous pouvez commencer avec ce qui précède et, espérons-le, le faire fonctionner.

La seule erreur cachée que je vois est que si vous ajoutez des vues enfants non-UIScrollView à RemorsefulScrollView, les événements tactiles que vous transmettez à un enfant peuvent vous revenir via la chaîne de répondeur, si l'enfant ne gère pas toujours les contacts comme UIScrollView. Une implémentation RemorsefulScrollView à l'épreuve des balles protégerait contre le touchesXxx .

Deuxième stratégie: Si, pour une raison quelconque, les UIScrollViews nestedes ne fonctionnent pas ou s'avèrent trop difficiles à obtenir, vous pouvez essayer de vous débrouiller avec un seul UIScrollView, en basculant sa propriété scrollViewDidScroll méthode déléguée scrollViewDidScroll .

Pour éviter le défilement diagonal, essayez d'abord de vous souvenir de contentOffset dans scrollViewWillBeginDragging et de vérifier et de réinitialiser contentOffset dans scrollViewDidScroll si vous détectez un mouvement en diagonale. Une autre stratégie à suivre consiste à réinitialiser contentSize pour ne permettre que le défilement dans une direction, une fois que vous avez déterminé la direction du doigt de l'utilisateur. (UIScrollView semble assez tolérant sur le fait de manipuler contentSize et contentOffset à partir de ses méthodes déléguées.)

Si cela ne fonctionne pas non plus ou si les résultats ne sont pas touchesBegan , vous devez ignorer les touchesBegan , touchesMoved etc. et ne pas transférer les événements de mouvement en diagonale vers UIScrollView. (L'expérience utilisateur sera toutefois sous-optimale dans ce cas, car vous devrez ignorer les mouvements diagonaux au lieu de les forcer dans une seule direction. Si vous vous sentez vraiment aventureux, vous pouvez écrire votre propre squelette UITouch, comme RevengeTouch. -C est tout simplement vieux C, et il n'y a rien d'autre de ducktypeful dans le monde que C: tant que personne ne vérifie la vraie classe des objects, ce que personne ne pense, vous pouvez faire en sorte que n'importe quelle classe ressemble à n'importe quelle autre classe. une possibilité de synthétiser les touches de votre choix, avec les coordonnées souhaitées.)

Stratégie de sauvegarde: il y a TTScrollView, une réimplémentation raisonnable de la bibliothèque UIScrollView dans Three20 . Malheureusement, il semble très naturel et non-iphonish pour l'utilisateur. Mais si chaque tentative d'utilisation de UIScrollView échoue, vous pouvez revenir à une vue de défilement personnalisée. Je le déconseille si possible; UIScrollView vous permet d’obtenir un aspect natif, quelle que soit son évolution dans les futures versions d’OS iPhone.

Ok, ce petit essai a pris un peu trop de temps. Je suis toujours dans les jeux UIScrollView après le travail sur ScrollingMadness il y a plusieurs jours.

PS Si vous travaillez avec l'un de ces outils et que vous avez envie de le partager, envoyez-moi le code correspondant par e-mail à andreyvit@gmail.com, je l'inclurai dans mon sac de trucs ScrollingMadness .

PPS Ajout de ce petit essai à ScrollingMadness README.

Cela fonctionne pour moi à chaque fois …

 scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * NumberOfPages, 1); 

Pour les paresseux comme moi:

Définissez scrollview.directionalLockEnabled sur YES

 - (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; } 

J’ai utilisé la méthode suivante pour résoudre ce problème, j’espère que cela vous aidera.

Définissez d’abord les variables suivantes dans votre fichier d’en-tête de contrôleur.

 CGPoint startPos; int scrollDirection; 

startPos conservera la valeur contentOffset lorsque votre délégué reçoit le message scrollViewWillBeginDragging . Donc, dans cette méthode, nous faisons cela;

 - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{ startPos = scrollView.contentOffset; scrollDirection=0; } 

Nous utilisons ensuite ces valeurs pour déterminer la direction de défilement des utilisateurs dans le message 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) 

enfin, nous devons arrêter toutes les opérations de mise à jour lorsque l'utilisateur finit de glisser;

  - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{ if (decelerate) { scrollDirection=3; } } 

Je suis peut-être en train de creuser ici, mais je suis tombé sur ce post aujourd’hui alors que j’essayais de résoudre le même problème. Voici ma solution, semble fonctionner très bien.

Je veux afficher un écran de contenu, mais permettre à l’utilisateur de faire défiler l’un des 4 autres écrans, vers le haut à gauche et à droite. J’ai un seul UIScrollView de taille 320×480 et une taille de contenu de 960×1440. Le décalage de contenu commence à 320 480. Dans scrollViewDidEndDecelerating :, je redessine les 5 vues (vues centrales et les 4 voisines) et réinitialise le décalage de contenu à 320 480.

Voici la viande de ma solution, la méthode 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); } } 

Les gars c’est aussi simple que de rendre la hauteur de la sous-vue identique à la hauteur de la vue de défilement et à la hauteur de la taille du contenu. Le défilement vertical est alors désactivé.

Voici mon implémentation d’un UIScrollView paginé qui n’autorise que des parchemins orthogonaux. Veillez à définir pagingEnabled sur YES .

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 

J’ai également dû résoudre ce problème, et bien que la réponse d’Andrey Tarantsov contienne de nombreuses informations utiles pour comprendre le fonctionnement de UIScrollViews , j’estime que la solution est un peu trop compliquée. Ma solution, qui n’implique aucun sous-classement ni transfert tactile, est la suivante:

  1. Créez deux vues de défilement nestedes, une pour chaque direction.
  2. Créez deux UIPanGestureRecognizers factices, encore une fois pour chaque direction.
  3. Créez des dépendances d’échec entre les propriétés panGestureRecognizer de UIScrollViews et la valeur factice UIPanGestureRecognizer correspondant à la direction perpendiculaire à la direction de défilement souhaitée de votre UIScrollView en utilisant -requireGestureRecognizerToFail: selector UIGestureRecognizer's -requireGestureRecognizerToFail: .
  4. Dans -gestureRecognizerShouldBegin: callbacks pour votre UIPanGestureRecognizers factice, calculez la direction initiale du panoramique à l’aide du sélecteur -translationInView: votre UIPanGestureRecognizers n’appelle pas ce rappel jusqu’à ce que votre touch ait suffisamment traduit pour vous enregistrer en tant que panoramique. Autoriser ou interdire à votre dispositif de reconnaissance de mouvements de commencer en fonction de la direction calculée, qui à son tour doit contrôler si la reconnaissance de mouvement panoramique UIScrollView perpendiculaire est autorisée ou non.
  5. Dans -gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer: n’autorisez pas vos UIPanGestureRecognizers factices à s’exécuter avec le défilement (sauf si vous souhaitez append un comportement supplémentaire).

Ce que j’ai fait a été de créer un UIScrollView de pagination avec un cadre de la taille de l’écran de visualisation et de définir la taille du contenu à la largeur requirejse pour tout mon contenu et à une hauteur de 1 (merci Tonetel). Ensuite, j’ai créé des pages de menu UIScrollView avec des frameworks définis sur chaque page, la taille de l’écran de visualisation, et défini la taille du contenu de chacun sur la largeur de l’écran et la hauteur nécessaire pour chacun. Ensuite, j’ai simplement ajouté chacune des pages de menu à la vue de pagination. Assurez-vous que la pagination est activée sur la vue de défilement de la pagination, mais désactivée sur les vues du menu (désactivée par défaut) et vous devriez être prêt.

La vue de défilement de la pagination défile maintenant horizontalement, mais pas verticalement en raison de la hauteur du contenu. Chaque page défile verticalement mais pas horizontalement en raison de ses contraintes de taille de contenu.

Voici le code si cette explication laissait à désirer:

 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; } 

Merci Tonetel. J’ai légèrement modifié votre approche, mais c’était exactement ce dont j’avais besoin pour empêcher le défilement horizontal.

 self.scrollView.contentSize = CGSizeMake(self.scrollView.contentSize.width, 1); 

C’est une solution améliorée d’icompot, qui était assez instable pour moi. Celui-ci fonctionne bien et est facile à mettre en œuvre:

 - (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; } 

De la documentation:

“la valeur par défaut est NO, ce qui signifie que le défilement est autorisé dans les directions horizontales et verticales. Si la valeur est OUI et que l’utilisateur commence à glisser dans une direction générale (horizontalement ou verticalement), le défilement est désactivé dans l’autre sens. ”

Je pense que la partie importante est “si … l’utilisateur commence à glisser dans une direction générale”. Donc, s’ils commencent à glisser en diagonale, cela ne se produit pas. Non pas que ces documents soient toujours fiables en matière d’interprétation – mais cela semble correspondre à ce que vous voyez.

Cela semble en fait raisonnable. Je dois demander – pourquoi voulez-vous limiter à l’horizontale ou seulement verticale à tout moment? Peut-être que UIScrollView n’est pas l’outil pour vous?

Ma vue se compose de 10 vues horizontales et certaines de ces vues se composent de plusieurs vues verticales. Vous obtiendrez donc quelque chose comme ceci:

 1.0 2.0 3.1 4.1 2.1 3.1 2.2 2.3 

Avec la pagination activée sur les axes horizontaux et verticaux

La vue a également les atsortingbuts suivants:

 [self setDelaysContentTouches:NO]; [self setMultipleTouchEnabled:NO]; [self setDirectionalLockEnabled:YES]; 

Maintenant, pour éviter le défilement diagonal, faites ceci:

 -(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]; } } 

Fonctionne comme un charme pour moi!

Nécessité de réinitialiser _isHorizontalScroll to NO in touchesEnded et touchesEnded .

Si vous avez un grand scrollview … et que vous souhaitez restreindre le défilement diagonal http://chandanshetty01.blogspot.in/2012/07/ressortingcting-diagonal-scrolling-in.html

Pour référence future, je me suis appuyé sur la réponse d’Andrey Tanasov et j’ai proposé la solution suivante qui fonctionne bien.

Définissez d’abord une variable pour stocker le contentOffset et définissez-le sur les coordonnées 0,0

 var positionScroll = CGPointMake(0, 0) 

Maintenant, implémentez les éléments suivants dans le délégué 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 } } } 

J'espère que cela t'aides.

Si vous n’avez pas essayé de le faire dans la version bêta 3.0, je vous recommande de tenter votre chance. J’utilise actuellement une série de vues de table dans une vue de défilement, et cela fonctionne exactement comme prévu. (Eh bien, le défilement le fait, de toute façon. J’ai trouvé cette question en cherchant une solution à un problème totalement différent …)

Pour ceux qui recherchent Swift at @AndreyTarantsov, la deuxième solution est la suivante:

  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 } } 

ce que nous faisons ici est de garder la position actuelle juste avant que scrollview soit glissé et ensuite vérifier si x a augmenté ou diminué par rapport à la position actuelle de x. Si oui, définissez pagingEnabled sur true, si non (y a augmenté / diminué), puis définissez sur pagingEnabled sur false.

J'espère que c'est utile pour les nouveaux venus!

Si vous utilisez le générateur d’interface pour concevoir votre interface, cela peut se faire assez facilement.

Dans le générateur d’interface, cliquez simplement sur UIScrollView et sélectionnez l’option (dans l’inspecteur) qui le limite au défilement à sens unique.