UICollectionView aligner la logique manquante dans le scrollview de pagination horizontale

J'ai un UICollectionView , qui fonctionne bien, jusqu'à ce que je commence à faire défiler. Voici quelques photos d'abord: entrez la description de l'image ici

Comme vous pouvez le voir, c'est génial. Au fur et à mesure que je commence à défiler (pagination activée), la première va un peu hors écran: entrez la description de l'image ici

C'est le problème. Originaly ma vue a 3 vues et je veux faire défiler et montrer 3 vues seulement. Mais comme il défile (pagination activée) il cache un peu de la première vue et montre peu de la première vue suivante de la page suivante.

Et voici une video, parce que c'est un peu difficile à expliquer: Vidéo du problème (Dropbox)

Voici une photo de mes parameters UICollectionView : entrez la description de l'image ici

Ça va être génial si quelqu'un peut aider!

Le problème fondamental est la disposition de stream n'est pas conçue pour soutenir la pagination. Pour réaliser l'effet de pagination, vous devrez sacrifier l'espace entre les cellules. Et calculez soigneusement le cadre des cellules et faites en sorte qu'il puisse être divisé par le cadre de vue de collection sans rests. J'expliquerai la raison.

Dire la disposition suivante est ce que vous vouliez.

entrez la description de l'image ici

Notez que la marge la plus à gauche (en vert) ne fait pas partie de l'espacement des cellules. Il est déterminé par l'encart de la section de disposition de stream. Étant donné que la disposition de stream ne prend pas en charge la valeur d'espacement hétérogène. Ce n'est pas une tâche sortingviale.

Par conséquent, après avoir réglé l'espacement et l'encart. La disposition suivante est ce que vous obtiendrez.

entrez la description de l'image ici

Après avoir fait défiler jusqu'à la page suivante. Vos cellules ne sont évidemment pas alignées comme prévu.

entrez la description de l'image ici

Faire l'espacement des cellules 0 peut résoudre ce problème. Cependant, il limite votre design si vous voulez la marge supplémentaire sur la page, surtout si la marge est différente de l'espacement des cellules. Cela nécessite également que le cadre de vue soit divisible par le cadre de la cellule. Parfois, c'est une douleur si votre cadre de vue n'est pas fixe (en considérant le cas de rotation).

La vraie solution est de sous-classr UICollectionViewFlowLayout et de replace les methods suivantes

 - (CGSize)collectionViewContentSize { // Only support single section for now. // Only support Horizontal scroll NSUInteger count = [self.collectionView.dataSource collectionView:self.collectionView numberOfItemsInSection:0]; CGSize canvasSize = self.collectionView.frame.size; CGSize contentSize = canvasSize; if (self.scrollDirection == UICollectionViewScrollDirectionHorizontal) { NSUInteger rowCount = (canvasSize.height - self.itemSize.height) / (self.itemSize.height + self.minimumInteritemSpacing) + 1; NSUInteger columnCount = (canvasSize.width - self.itemSize.width) / (self.itemSize.width + self.minimumLineSpacing) + 1; NSUInteger page = ceilf((CGFloat)count / (CGFloat)(rowCount * columnCount)); contentSize.width = page * canvasSize.width; } return contentSize; } - (CGRect)frameForItemAtIndexPath:(NSIndexPath *)indexPath { CGSize canvasSize = self.collectionView.frame.size; NSUInteger rowCount = (canvasSize.height - self.itemSize.height) / (self.itemSize.height + self.minimumInteritemSpacing) + 1; NSUInteger columnCount = (canvasSize.width - self.itemSize.width) / (self.itemSize.width + self.minimumLineSpacing) + 1; CGFloat pageMarginX = (canvasSize.width - columnCount * self.itemSize.width - (columnCount > 1 ? (columnCount - 1) * self.minimumLineSpacing : 0)) / 2.0f; CGFloat pageMarginY = (canvasSize.height - rowCount * self.itemSize.height - (rowCount > 1 ? (rowCount - 1) * self.minimumInteritemSpacing : 0)) / 2.0f; NSUInteger page = indexPath.row / (rowCount * columnCount); NSUInteger remainder = indexPath.row - page * (rowCount * columnCount); NSUInteger row = remainder / columnCount; NSUInteger column = remainder - row * columnCount; CGRect cellFrame = CGRectZero; cellFrame.origin.x = pageMarginX + column * (self.itemSize.width + self.minimumLineSpacing); cellFrame.origin.y = pageMarginY + row * (self.itemSize.height + self.minimumInteritemSpacing); cellFrame.size.width = self.itemSize.width; cellFrame.size.height = self.itemSize.height; if (self.scrollDirection == UICollectionViewScrollDirectionHorizontal) { cellFrame.origin.x += page * canvasSize.width; } return cellFrame; } - (UICollectionViewLayoutAtsortingbutes *)layoutAtsortingbutesForItemAtIndexPath:(NSIndexPath *)indexPath { UICollectionViewLayoutAtsortingbutes * attr = [super layoutAtsortingbutesForItemAtIndexPath:indexPath]; attr.frame = [self frameForItemAtIndexPath:indexPath]; return attr; } - (NSArray *)layoutAtsortingbutesForElementsInRect:(CGRect)rect { NSArray * originAttrs = [super layoutAtsortingbutesForElementsInRect:rect]; NSMutableArray * attrs = [NSMutableArray array]; [originAttrs enumerateObjectsUsingBlock:^(UICollectionViewLayoutAtsortingbutes * attr, NSUInteger idx, BOOL *stop) { NSIndexPath * idxPath = attr.indexPath; CGRect itemFrame = [self frameForItemAtIndexPath:idxPath]; if (CGRectIntersectsRect(itemFrame, rect)) { attr = [self layoutAtsortingbutesForItemAtIndexPath:idxPath]; [attrs addObject:attr]; } }]; return attrs; } 

Remarquez que l'extrait de code ci-dessus ne prend en charge qu'une seule section et la même direction de défilement horizontal. Mais ce n'est pas difficile à développer.

Aussi, si vous n'avez pas des millions de cellules. La caching de ces UICollectionViewLayoutAtsortingbutes peut être une bonne idée.

Vous pouvez désactiver la pagination sur UICollectionView et implémenter un mécanisme de défilement / pagination horizontal personnalisé avec une largeur / un décalage de page personnalisé comme celui-ci:

 - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset { float pageWidth = 210; float currentOffset = scrollView.contentOffset.x; float targetOffset = targetContentOffset->x; float newTargetOffset = 0; if (targetOffset > currentOffset) newTargetOffset = ceilf(currentOffset / pageWidth) * pageWidth; else newTargetOffset = floorf(currentOffset / pageWidth) * pageWidth; if (newTargetOffset < 0) newTargetOffset = 0; else if (newTargetOffset > scrollView.contentSize.width) newTargetOffset = scrollView.contentSize.width; targetContentOffset->x = currentOffset; [scrollView setContentOffset:CGPointMake(newTargetOffset, 0) animated:YES]; } 

Cette réponse est très tardive, mais je viens de jouer avec ce problème et j'ai découvert que la cause de la dérive est l' espacement des lignes . Si vous souhaitez que la page UICollectionView / FlowLayout apparaisse à des multiples exacts de la largeur de vos cellules, vous devez définir:

 UICollectionViewFlowLayout *flowLayout = (UICollectionViewFlowLayout *)collectionView.collectionViewLayout; flowLayout.minimumLineSpacing = 0.0; 

Vous ne penseriez pas que l'interlignage entre en jeu dans le défilement horizontal, mais apparemment c'est le cas.

Dans mon cas, j'expérimentais de gauche à droite, une cellule à la fois, sans espace entre les cellules. Chaque tour de la page introduit un petit peu de dérive de la position désirée, et il a semblé s'accumuler linéairement. ~ 10.0 pts par tour . J'ai réalisé 10.0 est la valeur par défaut de minimumLineSpacing dans la disposition de stream. Quand je l'ai mis à 0.0 , pas de dérive, quand je l'ai placé à la moitié de la largeur des limites, chaque page a dérivé une moitié supplémentaire des limites.

Changer le minimumInteritemSpacing n'a eu aucun effet .

edit – à partir de la documentation de UICollectionViewFlowLayout :

@property (non atomique) CGFloat minimumLineSpacing;

Discussion

Pour une grid défilant verticalement, cette valeur représente l'espacement minimum entre les rangées successives. Pour une grid à défilement horizontal, cette valeur représente l'espacement minimum entre les colonnes successives. Cet espacement n'est pas appliqué à l'espace entre l'en-tête et la première ligne ou entre la dernière ligne et le pied de page.

La valeur par défaut de cette propriété est 10.0.

entrez la description de l'image ici

La solution de l'article suivant est élégante et simple. L'idée principale est la création de scrollView au-dessus de votre collectionView avec l'envoi de toutes les valeurs contentOffset.

http://b2cloud.com.au/tutorial/uiscrollview-paging-size/

Il faut dire en mettant en œuvre cette méthode:

 - (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity; 

Je n'ai pas réalisé une animation fluide comme cela se produit avec pagingEnabled = YES.

Je sais que cette question est ancienne, mais pour quiconque tombe sur ce point.

Tout ce que vous avez à faire pour corriger cela est de mettre le MinimumInterItemSpacing à 0 et diminuer le cadre du contenu.

Je pense que je comprends le problème. Je vais essayer de te le faire comprendre aussi.

Si vous y regardez de plus près, vous verrez que ce problème ne se produit que graduellement et pas seulement sur la première page.

entrez la description de l'image ici

Si je comprends bien, dans votre application, actuellement, chaque élément UICollectionView sont ces boîtes arrondies que nous voyons, et vous avez un décalage / marge entre eux qui est constante. C'est ce qui cause le problème.

Au lieu de cela, ce que vous devez faire est de créer un élément UICollectionView qui est 1/3 de la largeur de l'set de la vue, puis d'append cette vue d'image arrondie à l'intérieur. Pour faire reference à l'image, la couleur verte devrait être votre UICollectionViewItem et non le noir.

@devdavid était sur place sur le flowLayout.minimumLineSpacing à zéro.

Cela peut également être fait dans l'éditeur de disposition, en définissant l'espacement minimal des lignes sur 0:

Manière plus facile

Avez-vous votre propre UICollectionViewFlowLayout ?

Si c'est le cas, append -(CGPoint) targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity vous aidera à calculer où le scrollview devrait s'arrêter.

Cela pourrait fonctionner (NB: UNTESTED!):

 -(CGPoint) targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity { CGFloat offsetAdjustment = MAXFLOAT; CGFloat targetX = proposedContentOffset.x + self.minimumInteritemSpacing + self.sectionInset.left; CGRect targetRect = CGRectMake(proposedContentOffset.x, 0.0, self.collectionView.bounds.size.width, self.collectionView.bounds.size.height); NSArray *array = [super layoutAtsortingbutesForElementsInRect:targetRect]; for(UICollectionViewLayoutAtsortingbutes *layoutAtsortingbutes in array) { if(layoutAtsortingbutes.representedElementCategory == UICollectionElementCategoryCell) { CGFloat itemX = layoutAtsortingbutes.frame.origin.x; if (ABS(itemX - targetX) < ABS(offsetAdjustment)) { offsetAdjustment = itemX - targetX; } } } return CGPointMake(proposedContentOffset.x + offsetAdjustment, proposedContentOffset.y); } 

Ma réponse est basée sur la réponse https://stackoverflow.com/a/27242179/440168 mais est plus simple.

entrez la description de l'image ici

Vous devez placer UIScrollView au-dessus de UICollectionView et leur donner des tailles égales:

 @property (nonatomic, weak) IBOutlet UICollectionView *collectionView; @property (nonatomic, weak) IBOutlet UIScrollView *scrollView; 

Configurez ensuite contentInset de la vue de la collection, par exemple:

 CGFloat inset = self.view.bounds.size.width*2/9; self.collectionView.contentInset = UIEdgeInsetsMake(0, inset, 0, inset); 

Et contentSize de la vue contentSize :

 self.scrollView.contentSize = CGSizeMake(self.placesCollectionView.bounds.size.width*[self.collectionView numberOfItemsInSection:0],0); 

N'oubliez pas de définir le délégué de la vue déroulante:

 self.scrollView.delegate = self; 

Et implémentez la magie principale:

 - (void)scrollViewDidScroll:(UIScrollView *)scrollView { if (scrollView == self.scrollView) { CGFloat inset = self.view.bounds.size.width*2/9; CGFloat scale = (self.placesCollectionView.bounds.size.width-2*inset)/scrollView.bounds.size.width; self.collectionView.contentOffset = CGPointMake(scrollView.contentOffset.x*scale - inset, 0); } } 

La largeur de votre UICollectionView devrait être une multiplication exacte de la largeur de la taille de la cellule + les encarts gauche et droit. Dans votre exemple, si la largeur de la cellule est 96, alors la UICollectionView de UICollectionView doit être (96 + 5 + 5) * 3 = 318 . Ou, si vous souhaitez conserver la largeur 320 de UICollectionView, la largeur de votre cellule doit être 320 / 3 - 5 - 5 = 96.666 .

Si cela ne vous aide pas, la UICollectionView votre UICollectionView peut être différente de celle qui est définie dans le file xib, lorsque l'application s'exécute. Pour vérifier cela – ajoutez une instruction NSLog pour imprimer la taille de la vue en cours d'exécution:

 NSLog(@"%@", NSSsortingngFromCGRect(uiContentViewController.view.frame)); 

C'est le même problème que je connaissais et j'ai posté ma solution sur un autre post, donc je le postrai à nouveau ici.

J'ai trouvé une solution, et cela impliquait de sous-classr UICollectionViewFlowLayout.

Ma taille de CollectionViewCell est 302 X 457 et j'ai mis mon interligne minimum à 18 (9pix pour chaque cellule)

Lorsque vous étendez cette class, il existe quelques methods qui doivent être surchargées. L'un d'eux est

  • (CGSize) collectionViewContentSize

Dans cette méthode, j'ai dû additionner la largeur totale de ce qui était dans UICollectionView. Cela inclut le ([nombre de sources de données] * widthOfCollectionViewCell) + ([nombre de sources de données] * 18)

Voici mes methods personnalisées UICollectionViewFlowLayout ….

 -(id)init { if((self = [super init])){ self.itemSize = CGSizeMake(302, 457); self.sectionInset = UIEdgeInsetsMake(10, 10, 10, 10); self.minimumInteritemSpacing = 0.0f; self.minimumLineSpacing = 18.0f; [self setScrollDirection:UICollectionViewScrollDirectionHorizontal]; } return self; } -(CGSize)collectionViewContentSize{ return CGSizeMake((numCellsCount * 302)+(numCellsCount * 18), 457); } 

Cela a fonctionné pour moi, alors j'espère que quelqu'un d'autre le trouve utile!

Vous pouvez également définir la largeur de la vue sur "espacement + 320", puis définir l'activation de la page sur Oui. il fera défiler '320 + espacement' pour chaque fois. Je le pense parce que la page active fera défiler la largeur de la vue mais pas la largeur de l'écran.

Je pense avoir une solution à ce problème. Mais je ne sais pas si c'est le meilleur.

UICollectionViewFlowLayout contient une propriété appelée sectionInset . Vous pouvez donc définir la section Encadré en fonction de vos besoins et créer une page correspondant à une section. Par conséquent votre défilement devrait s'ajuster automatiquement dans les pages (= sections)

J'ai eu un problème similaire avec la pagination. Même si les insertions de cellule étaient toutes 0 et la cellule avait exactement la même taille en largeur et en hauteur que l' UICollectionView , la pagination n'était pas correcte.

Ce que j'ai remarqué ressemble à un bug dans cette version ( UICollectionView , iOS 6): je pouvais voir que si je travaillais avec un UICollectionView avec width = 310px ou plus, et une hauteur = 538px, j'étais en difficulté. Cependant, si j'ai diminué la largeur à, disons, 300px (même hauteur), j'ai fait fonctionner les choses parfaitement!

Pour une reference future, j'espère que ça aide!

J'ai rencontré un problème similaire lorsque j'essayais de faire fonctionner la pagination horizontale sur une grid de cellules de 3 x 3 avec des encarts de section et l'espacement des lignes et des lignes.

La réponse pour moi (après avoir essayé beaucoup de suggestions – y compris sous-classr UICollectionViewFlowLayout et diverses solutions déléguées UIScrollView) était simple. J'ai simplement utilisé des sections dans UICollectionView en décomposant mon set de données en sections de 9 éléments (ou less), et en utilisant les methods de source de données numberOfSectionsInCollectionView et numberOfItemsInSection UICollectionView.

La pagination horizontale de UICollectionView fonctionne maintenant magnifiquement. Je recommand cette approche à tous ceux qui se déchirent actuellement les cheveux sur un scénario similaire.

Si vous utilisez la disposition de stream par défaut pour votre UICollectionView et que vous ne voulez PAS d'espace entre chaque cellule, vous pouvez définir sa propriété miniumumLineSpacing sur 0 via:

 ((UICollectionViewFlowLayout *) self.collectionView.collectionViewLayout).minimumLineSpacing = 0;