Aligner les cellules à gauche dans UICollectionView

J'utilise un UICollectionView dans mon projet, où il y a plusieurs cellules de différentes largeurs sur une ligne. Selon: https://developer.apple.com/library/content/documentation/WindowsViews/Conceptual/CollectionViewPGforIOS/UsingtheFlowLayout/UsingtheFlowLayout.html

Il répartit les cellules sur toute la ligne avec un rembourrage égal. Cela se passe comme prévu, sauf que je veux les justifier, et coder en dur une largeur de remplissage.

Je suppose que j'ai besoin de sous-classr UICollectionViewFlowLayout, cependant après avoir lu certains des tutoriels en ligne, je ne vois pas comment cela fonctionne.

Les autres solutions ici ne fonctionnent pas correctement lorsque la ligne est composée de seulement 1 object ou est compliquée.

Sur la base de l'exemple donné par Ryan, j'ai changé le code pour détecter une nouvelle ligne en inspectant la position Y du nouvel élément. Très simple et rapide dans la performance.

Rapide:

class LeftAlignedCollectionViewFlowLayout: UICollectionViewFlowLayout { override func layoutAtsortingbutesForElements(in rect: CGRect) -> [UICollectionViewLayoutAtsortingbutes]? { let atsortingbutes = super.layoutAtsortingbutesForElements(in: rect) var leftMargin = sectionInset.left var maxY: CGFloat = -1.0 atsortingbutes?.forEach { layoutAtsortingbute in if layoutAtsortingbute.frame.origin.y >= maxY { leftMargin = sectionInset.left } layoutAtsortingbute.frame.origin.x = leftMargin leftMargin += layoutAtsortingbute.frame.width + minimumInteritemSpacing maxY = max(layoutAtsortingbute.frame.maxY , maxY) } return atsortingbutes } } 

Objectif c:

 - (NSArray *)layoutAtsortingbutesForElementsInRect:(CGRect)rect { NSArray *atsortingbutes = [super layoutAtsortingbutesForElementsInRect:rect]; CGFloat leftMargin = self.sectionInset.left; //initalized to silence comstackr, and actaully safer, but not planning to use. CGFloat maxY = -1.0f; //this loop assumes atsortingbutes are in IndexPath order for (UICollectionViewLayoutAtsortingbutes *atsortingbute in atsortingbutes) { if (atsortingbute.frame.origin.y >= maxY) { leftMargin = self.sectionInset.left; } atsortingbute.frame = CGRectMake(leftMargin, atsortingbute.frame.origin.y, atsortingbute.frame.size.width, atsortingbute.frame.size.height); leftMargin += atsortingbute.frame.size.width + self.minimumInteritemSpacing; maxY = MAX(CGRectGetMaxY(atsortingbute.frame), maxY); } return atsortingbutes; } 

La question a pris un certain time mais il n'y a pas de réponse et c'est une bonne question. La réponse consiste à replace une méthode dans la sous-class UICollectionViewFlowLayout:

 @implementation MYFlowLayoutSubclass //Note, the layout's minimumInteritemSpacing (default 10.0) should not be less than this. #define ITEM_SPACING 10.0f - (NSArray *)layoutAtsortingbutesForElementsInRect:(CGRect)rect { NSArray *atsortingbutesForElementsInRect = [super layoutAtsortingbutesForElementsInRect:rect]; NSMutableArray *newAtsortingbutesForElementsInRect = [[NSMutableArray alloc] initWithCapacity:atsortingbutesForElementsInRect.count]; CGFloat leftMargin = self.sectionInset.left; //initalized to silence comstackr, and actaully safer, but not planning to use. //this loop assumes atsortingbutes are in IndexPath order for (UICollectionViewLayoutAtsortingbutes *atsortingbutes in atsortingbutesForElementsInRect) { if (atsortingbutes.frame.origin.x == self.sectionInset.left) { leftMargin = self.sectionInset.left; //will add outside loop } else { CGRect newLeftAlignedFrame = atsortingbutes.frame; newLeftAlignedFrame.origin.x = leftMargin; atsortingbutes.frame = newLeftAlignedFrame; } leftMargin += atsortingbutes.frame.size.width + ITEM_SPACING; [newAtsortingbutesForElementsInRect addObject:atsortingbutes]; } return newAtsortingbutesForElementsInRect; } @end 

Comme recommandé par Apple, vous obtenez les attributes de layout de super et itérer sur eux. Si c'est le premier dans la rangée (défini par son origine.x étant sur la marge de gauche), vous le laissez seul et réinitialisez le x à zéro. Ensuite, pour la première cellule et chaque cellule, vous ajoutez la largeur de cette cellule plus une marge. Ceci est passé à l'élément suivant dans la boucle. Si ce n'est pas le premier élément, vous définissez son origine.x sur la marge calculée en cours d'exécution et ajoutez de nouveaux éléments au tableau.

J'ai eu le même problème, essayez le Cocoapod UICollectionViewLeftAlignedLayout . Il suffit de l'inclure dans votre projet et de l'initialiser comme ceci:

 UICollectionViewLeftAlignedLayout *layout = [[UICollectionViewLeftAlignedLayout alloc] init]; UICollectionView *leftAlignedCollectionView = [[UICollectionView alloc] initWithFrame:frame collectionViewLayout:layout]; 

Sur la base de la réponse de Michael Sand , j'ai créé une bibliothèque UICollectionViewFlowLayout sous- UICollectionViewFlowLayout pour faire une justification horizontale gauche, droite ou complète (fondamentalement la valeur par défaut) – elle permet également de définir la distance absolue entre chaque cellule. Je prévois d'append une justification du centre horizontal et une justification verticale.

https://github.com/eroth/ERJustifiedFlowLayout

En rapide. Selon Michaels réponse

 override func layoutAtsortingbutesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAtsortingbutes]? { guard let oldAtsortingbutes = super.layoutAtsortingbutesForElementsInRect(rect) else { return super.layoutAtsortingbutesForElementsInRect(rect) } let spacing = CGFloat(50) // REPLACE WITH WHAT SPACING YOU NEED var newAtsortingbutes = [UICollectionViewLayoutAtsortingbutes]() var leftMargin = self.sectionInset.left for atsortingbutes in oldAtsortingbutes { if (atsortingbutes.frame.origin.x == self.sectionInset.left) { leftMargin = self.sectionInset.left } else { var newLeftAlignedFrame = atsortingbutes.frame newLeftAlignedFrame.origin.x = leftMargin atsortingbutes.frame = newLeftAlignedFrame } leftMargin += atsortingbutes.frame.width + spacing newAtsortingbutes.append(atsortingbutes) } return newAtsortingbutes } 

Il y a beaucoup de bonnes idées incluses dans les réponses à cette question. Cependant, la plupart d'entre eux ont quelques inconvénients:

  • Les solutions qui ne vérifient pas la valeur y de la cellule ne fonctionnent que pour les mises en page sur une seule ligne . Ils échouent pour les dispositions de vue de collection avec plusieurs lignes.
  • Les solutions qui vérifient la valeur y comme la réponse d'Angel García Olloqui ne fonctionnent que si toutes les cellules ont la même hauteur . Ils échouent pour les cellules avec une hauteur variable.
  • La plupart des solutions remplacent uniquement la fonction layoutAtsortingbutesForElements(in rect: CGRect) . Ils ne remplacent pas layoutAtsortingbutesForItem(at indexPath: IndexPath) . Ceci est un problème car la vue de collecte appelle périodiquement cette dernière fonction pour extraire les attributes de disposition pour un path d'index particulier. Si vous ne renvoyez pas les attributes appropriés à partir de cette fonction, vous risquez de rencontrer toutes sortes de bogues visuels, par exemple lors des animations d'insertion et de suppression de cellules ou lors de l'utilisation de cellules auto-dimensionnables en définissant la propriété estimatedItemSize vue de collection. L'état des docs Apple :

    Chaque object de layout personnalisé doit implémenter la méthode layoutAtsortingbutesForItemAtIndexPath:

  • De nombreuses solutions émettent également des hypothèses sur le paramètre rect qui est transmis à la fonction layoutAtsortingbutesForElements(in rect: CGRect) . Par exemple, beaucoup sont basés sur l'hypothèse que le rect commence toujours au début d'une nouvelle ligne ce qui n'est pas forcément le cas.

Donc en d'autres termes:

La plupart des solutions proposées sur cette page fonctionnent pour certaines applications spécifiques, mais elles ne fonctionnent pas comme prévu dans toutes les situations.


AlignedCollectionViewFlowLayout

Afin de résoudre ces problèmes, j'ai créé une sous-class UICollectionViewFlowLayout qui suit une idée similaire à celle suggérée par Matt et Chris Wagner dans leurs réponses à une question similaire. Il peut soit aligner les cellules

⬅︎ à gauche :

Disposition alignée à gauche

ou ➡︎ droite :

Disposition alignée à droite

et offre en outre des options pour aligner verticalement les cellules dans leurs rangées respectives (au cas où elles varient en hauteur).

Vous pouvez simplement le download ici:

https://github.com/mischa-hildebrand/AlignedCollectionViewFlowLayout

L'utilisation est directe et expliquée dans le file README. Vous créez essentiellement une instance de AlignedCollectionViewFlowLayout , spécifiez l'alignment souhaité et affectez-le à la propriété collectionViewLayout de votre vue de collectionViewLayout :

  let alignedFlowLayout = AlignedCollectionViewFlowLayout(horizontalAlignment: .left, verticalAlignment: .top) yourCollectionView.collectionViewLayout = alignedFlowLayout 

(Il est également disponible sur Cocoapods .)


Comment ça marche (pour les cellules alignées à gauche):

Le concept ici est de s'appuyer uniquement sur la fonction layoutAtsortingbutesForItem(at indexPath: IndexPath) . Dans layoutAtsortingbutesForElements(in rect: CGRect) nous obtenons simplement les paths d'index de toutes les cellules du rect , puis appelons la première fonction pour chaque path d'index pour récupérer les trames correctes:

 override public func layoutAtsortingbutesForElements(in rect: CGRect) -> [UICollectionViewLayoutAtsortingbutes]? { // We may not change the original layout atsortingbutes // or UICollectionViewFlowLayout might complain. let layoutAtsortingbutesObjects = copy(super.layoutAtsortingbutesForElements(in: rect)) layoutAtsortingbutesObjects?.forEach({ (layoutAtsortingbutes) in if layoutAtsortingbutes.representedElementCategory == .cell { // Do not modify header views etc. let indexPath = layoutAtsortingbutes.indexPath // Resortingeve the correct frame from layoutAtsortingbutesForItem(at: indexPath): if let newFrame = layoutAtsortingbutesForItem(at: indexPath)?.frame { layoutAtsortingbutes.frame = newFrame } } }) return layoutAtsortingbutesObjects } 

(La fonction copy() crée simplement une copy complète de tous les attributes de disposition dans le tableau, vous pouvez regarder dans le code source pour son implémentation.)

Alors maintenant, la seule chose que nous devons faire est d'implémenter la fonction layoutAtsortingbutesForItem(at indexPath: IndexPath) correctement. La super class UICollectionViewFlowLayout place déjà le nombre correct de cellules dans chaque ligne, il suffit donc de les déplacer à gauche dans leur ligne respective. La difficulté réside dans le calcul de la quantité d'espace dont nous avons besoin pour déplacer chaque cellule vers la gauche.

Comme nous voulons avoir un espacement fixe entre les cellules, l'idée centrale est de supposer que la cellule précédente (la cellule restante de la cellule actuellement disposée) est déjà positionnée correctement. Ensuite, il suffit d'append l'espacement des cellules à la valeur maxX l' maxX de la cellule précédente et c'est la valeur origin.x pour l'image de la cellule courante.

Maintenant nous avons juste besoin de savoir quand nous avons atteint le début d'une ligne, de sorte que nous n'alignions pas une cellule à côté d'une cellule dans la ligne précédente. (Cela entraînerait non seulement une layout incorrecte, mais aussi extrêmement laggy.) Nous devons donc avoir une ancre de récursivité. L'approche que j'utilise pour find cette ancre de récursivité est la suivante:

Pour savoir si la cellule à l'index i est dans la même ligne que la cellule avec l'indice i-1

  +---------+----------------------------------------------------------------+---------+ | | | | | | +------------+ | | | | | | | | | section |- - -|- - - - - - |- - - - +---------------------+ - - - - - - -| section | | inset | |intersection| | | line rect | inset | | |- - -|- - - - - - |- - - - +---------------------+ - - - - - - -| | | (left) | | | current item | (right) | | | +------------+ | | | | previous item | | +---------+----------------------------------------------------------------+---------+ 

… Je "dessine" un rectangle autour de la cellule actuelle et l'étire sur toute la largeur de la vue de la collection. Comme UICollectionViewFlowLayout centre toutes les cellules verticalement, chaque cellule de la même ligne doit croiser ce rectangle.

Ainsi, je vérifie simplement si la cellule avec l'indice i-1 croise ce rectangle de ligne créé à partir de la cellule avec l'indice i .

  • Si elle se recoupe, la cellule avec l'indice i n'est pas la cellule la plus à gauche de la ligne.
    → Récupère le cadre de la cellule précédente (avec l'index i-1 ) et déplace la cellule courante à côté de celle-ci.

  • Si elle ne se croise pas, la cellule avec l'indice i est la cellule la plus à gauche de la ligne.
    → Déplacez la cellule vers le bord gauche de la vue de collection (sans changer sa position verticale).

Je ne publierai pas l'implémentation réelle de la fonction layoutAtsortingbutesForItem(at indexPath: IndexPath) ici car je pense que la partie la plus importante est de comprendre l' idée et vous pouvez toujours vérifier mon implémentation dans le code source . (C'est un peu plus compliqué qu'expliqué ici car .right également l'alignment .right et diverses options d'alignment vertical, mais il suit la même idée.)


Wow, je suppose que c'est la plus longue réponse que j'ai jamais écrite sur Stackoverflow. J'espère que ça aide. 😉

Voici la réponse originale dans Swift. Cela fonctionne toujours très bien.

 class LeftAlignedFlowLayout: UICollectionViewFlowLayout { private override func layoutAtsortingbutesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAtsortingbutes]? { let atsortingbutes = super.layoutAtsortingbutesForElementsInRect(rect) var leftMargin = sectionInset.left atsortingbutes?.forEach { layoutAtsortingbute in if layoutAtsortingbute.frame.origin.x == sectionInset.left { leftMargin = sectionInset.left } else { layoutAtsortingbute.frame.origin.x = leftMargin } leftMargin += layoutAtsortingbute.frame.width + minimumInteritemSpacing } return atsortingbutes } } 

Exception: Autosizing Cells

Il y a malheureusement une grosse exception. Si vous utilisez UICollectionViewFlowLayout 's estimatedItemSize . En interne, UICollectionViewFlowLayout change un peu les choses. Je ne l'ai pas suivi complètement, mais il est clair qu'il appelle d'autres methods après layoutAtsortingbutesForElementsInRect lors de l'auto-dimensionnement des cellules. De mon essai et erreur, j'ai trouvé qu'il semble appeler layoutAtsortingbutesForItemAtIndexPath pour chaque cellule individuellement lors de l'autosizing plus souvent. Cette mise à jour LeftAlignedFlowLayout fonctionne très bien avec estimatedItemSize . Il fonctionne également avec des cellules de taille statique, mais les appels de layout supplémentaires me conduit à utiliser la réponse d'origine chaque fois que je n'ai pas besoin de cellules autosizing.

 class LeftAlignedFlowLayout: UICollectionViewFlowLayout { private override func layoutAtsortingbutesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAtsortingbutes? { let layoutAtsortingbute = super.layoutAtsortingbutesForItemAtIndexPath(indexPath)?.copy() as? UICollectionViewLayoutAtsortingbutes // First in a row. if layoutAtsortingbute?.frame.origin.x == sectionInset.left { return layoutAtsortingbute } // We need to align it to the previous item. let previousIndexPath = NSIndexPath(forItem: indexPath.item - 1, inSection: indexPath.section) guard let previousLayoutAtsortingbute = self.layoutAtsortingbutesForItemAtIndexPath(previousIndexPath) else { return layoutAtsortingbute } layoutAtsortingbute?.frame.origin.x = previousLayoutAtsortingbute.frame.maxX + self.minimumInteritemSpacing return layoutAtsortingbute } } 

Merci pour la réponse de Michael Sand . Je l'ai modifié à une solution de plusieurs rangées (le même alignment Top y de chaque rangée) qui est l'alignment gauche, même l'espacement à chaque élément.

 static CGFloat const ITEM_SPACING = 10.0f; - (NSArray *)layoutAtsortingbutesForElementsInRect:(CGRect)rect { CGRect contentRect = {CGPointZero, self.collectionViewContentSize}; NSArray *atsortingbutesForElementsInRect = [super layoutAtsortingbutesForElementsInRect:contentRect]; NSMutableArray *newAtsortingbutesForElementsInRect = [[NSMutableArray alloc] initWithCapacity:atsortingbutesForElementsInRect.count]; CGFloat leftMargin = self.sectionInset.left; //initalized to silence comstackr, and actaully safer, but not planning to use. NSMutableDictionary *leftMarginDictionary = [[NSMutableDictionary alloc] init]; for (UICollectionViewLayoutAtsortingbutes *atsortingbutes in atsortingbutesForElementsInRect) { UICollectionViewLayoutAtsortingbutes *attr = atsortingbutes.copy; CGFloat lastLeftMargin = [[leftMarginDictionary valueForKey:[[NSNumber numberWithFloat:atsortingbutes.frame.origin.y] ssortingngValue]] floatValue]; if (lastLeftMargin == 0) lastLeftMargin = leftMargin; CGRect newLeftAlignedFrame = attr.frame; newLeftAlignedFrame.origin.x = lastLeftMargin; attr.frame = newLeftAlignedFrame; lastLeftMargin += attr.frame.size.width + ITEM_SPACING; [leftMarginDictionary setObject:@(lastLeftMargin) forKey:[[NSNumber numberWithFloat:atsortingbutes.frame.origin.y] ssortingngValue]]; [newAtsortingbutesForElementsInRect addObject:attr]; } return newAtsortingbutesForElementsInRect; } 

Basé sur toutes les réponses, je change un peu et ça marche bien pour moi

 override func layoutAtsortingbutesForElements(in rect: CGRect) -> [UICollectionViewLayoutAtsortingbutes]? { let atsortingbutes = super.layoutAtsortingbutesForElements(in: rect) var leftMargin = sectionInset.left var maxY: CGFloat = -1.0 atsortingbutes?.forEach { layoutAtsortingbute in if layoutAtsortingbute.frame.origin.y >= maxY || layoutAtsortingbute.frame.origin.x == sectionInset.left { leftMargin = sectionInset.left } if layoutAtsortingbute.frame.origin.x == sectionInset.left { leftMargin = sectionInset.left } else { layoutAtsortingbute.frame.origin.x = leftMargin } leftMargin += layoutAtsortingbute.frame.width maxY = max(layoutAtsortingbute.frame.maxY, maxY) } return atsortingbutes } 

Le problème avec UICollectionView est qu'il essaye d'ajuster automatiquement les cellules dans la zone disponible. J'ai fait cela en commençant par définir le nombre de lignes et de colonnes, puis en définissant la taille de la cellule pour cette ligne et cette colonne

1) Pour définir les sections (lignes) de mon UICollectionView:

 (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView 

2) Pour définir le nombre d'éléments dans une section. Vous pouvez définir un nombre différent d'éléments pour chaque section. vous pouvez get le numéro de section en utilisant le paramètre 'section'.

 (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section 

3) Définir la taille de la cellule pour chaque section et chaque rangée séparément. Vous pouvez get le numéro de section et le numéro de ligne en utilisant le paramètre 'indexPath', c'est-à-dire [indexPath section] pour le numéro de section et [indexPath row] pour le numéro de ligne.

 (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath 

4) Ensuite, vous pouvez afficher vos cellules dans des lignes et des sections en utilisant:

 (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath 

NOTE: Dans UICollectionView

 Section == Row IndexPath.Row == Column 

La réponse de Mike Sand est bonne mais j'ai eu quelques problèmes avec ce code (comme une longue cellule). Et nouveau code:

 #define ITEM_SPACE 7.0f @implementation LeftAlignedCollectionViewFlowLayout - (NSArray *)layoutAtsortingbutesForElementsInRect:(CGRect)rect { NSArray* atsortingbutesToReturn = [super layoutAtsortingbutesForElementsInRect:rect]; for (UICollectionViewLayoutAtsortingbutes* atsortingbutes in atsortingbutesToReturn) { if (nil == atsortingbutes.representedElementKind) { NSIndexPath* indexPath = atsortingbutes.indexPath; atsortingbutes.frame = [self layoutAtsortingbutesForItemAtIndexPath:indexPath].frame; } } return atsortingbutesToReturn; } - (UICollectionViewLayoutAtsortingbutes *)layoutAtsortingbutesForItemAtIndexPath:(NSIndexPath *)indexPath { UICollectionViewLayoutAtsortingbutes* currentItemAtsortingbutes = [super layoutAtsortingbutesForItemAtIndexPath:indexPath]; UIEdgeInsets sectionInset = [(UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout sectionInset]; if (indexPath.item == 0) { // first item of section CGRect frame = currentItemAtsortingbutes.frame; frame.origin.x = sectionInset.left; // first item of the section should always be left aligned currentItemAtsortingbutes.frame = frame; return currentItemAtsortingbutes; } NSIndexPath* previousIndexPath = [NSIndexPath indexPathForItem:indexPath.item-1 inSection:indexPath.section]; CGRect previousFrame = [self layoutAtsortingbutesForItemAtIndexPath:previousIndexPath].frame; CGFloat previousFrameRightPoint = previousFrame.origin.x + previousFrame.size.width + ITEM_SPACE; CGRect currentFrame = currentItemAtsortingbutes.frame; CGRect strecthedCurrentFrame = CGRectMake(0, currentFrame.origin.y, self.collectionView.frame.size.width, currentFrame.size.height); if (!CGRectIntersectsRect(previousFrame, strecthedCurrentFrame)) { // if current item is the first item on the line // the approach here is to take the current frame, left align it to the edge of the view // then stretch it the width of the collection view, if it intersects with the previous frame then that means it // is on the same line, otherwise it is on it's own new line CGRect frame = currentItemAtsortingbutes.frame; frame.origin.x = sectionInset.left; // first item on the line should always be left aligned currentItemAtsortingbutes.frame = frame; return currentItemAtsortingbutes; } CGRect frame = currentItemAtsortingbutes.frame; frame.origin.x = previousFrameRightPoint; currentItemAtsortingbutes.frame = frame; return currentItemAtsortingbutes; } 

Editer la réponse d'Angel García Olloqui pour respecter minimumInteritemSpacing de collectionView(_:layout:minimumInteritemSpacingForSectionAt:) de delegate collectionView(_:layout:minimumInteritemSpacingForSectionAt:) , s'il l'implémente.

 override func layoutAtsortingbutesForElements(in rect: CGRect) -> [UICollectionViewLayoutAtsortingbutes]? { let atsortingbutes = super.layoutAtsortingbutesForElements(in: rect) var leftMargin = sectionInset.left var maxY: CGFloat = -1.0 atsortingbutes?.forEach { layoutAtsortingbute in if layoutAtsortingbute.frame.origin.y >= maxY { leftMargin = sectionInset.left } layoutAtsortingbute.frame.origin.x = leftMargin let delegate = collectionView?.delegate as? UICollectionViewDelegateFlowLayout let spacing = delegate?.collectionView?(collectionView!, layout: self, minimumInteritemSpacingForSectionAt: 0) ?? minimumInteritemSpacing leftMargin += layoutAtsortingbute.frame.width + spacing maxY = max(layoutAtsortingbute.frame.maxY , maxY) } return atsortingbutes } 

Basé sur les réponses ici, mais fixe les accidents et les problèmes d'alignment lorsque votre vue de collection est également obtenu en-têtes ou pieds de page. Aligner les cellules de gauche uniquement:

 class LeftAlignedCollectionViewFlowLayout: UICollectionViewFlowLayout { override func layoutAtsortingbutesForElements(in rect: CGRect) -> [UICollectionViewLayoutAtsortingbutes]? { let atsortingbutes = super.layoutAtsortingbutesForElements(in: rect) var leftMargin = sectionInset.left var prevMaxY: CGFloat = -1.0 atsortingbutes?.forEach { layoutAtsortingbute in guard layoutAtsortingbute.representedElementCategory == .cell else { return } if layoutAtsortingbute.frame.origin.y >= prevMaxY { leftMargin = sectionInset.left } layoutAtsortingbute.frame.origin.x = leftMargin leftMargin += layoutAtsortingbute.frame.width + minimumInteritemSpacing prevMaxY = layoutAtsortingbute.frame.maxY } return atsortingbutes } } 

Le code ci-dessus fonctionne pour moi. Je voudrais partager le code Swift 3.0 respectif.

 class SFFlowLayout: UICollectionViewFlowLayout { let itemSpacing: CGFloat = 3.0 override func layoutAtsortingbutesForElements(in rect: CGRect) -> [UICollectionViewLayoutAtsortingbutes]? { let atsortinguteElementsInRect = super.layoutAtsortingbutesForElements(in: rect) var newAtsortingbuteForElement: Array<UICollectionViewLayoutAtsortingbutes> = [] var leftMargin = self.sectionInset.left for tempAtsortingbute in atsortinguteElementsInRect! { let atsortingbute = tempAtsortingbute if atsortingbute.frame.origin.x == self.sectionInset.left { leftMargin = self.sectionInset.left } else { var newLeftAlignedFrame = atsortingbute.frame newLeftAlignedFrame.origin.x = leftMargin atsortingbute.frame = newLeftAlignedFrame } leftMargin += atsortingbute.frame.size.width + itemSpacing newAtsortingbuteForElement.append(atsortingbute) } return newAtsortingbuteForElement } }