79 votes

UICollectionView flowLayout n'enveloppe pas correctement les cellules

J'ai un UICollectionView avec un FLowLayout. La plupart du temps, cela fonctionne comme prévu, mais de temps en temps, l'une des cellules ne s'enroule pas correctement. Par exemple, la cellule qui devrait se trouver dans la première "colonne" de la troisième rangée se trouve en fait à la traîne dans la deuxième rangée et il y a juste un espace vide là où elle devrait se trouver (voir le diagramme ci-dessous). Tout ce que vous pouvez voir de cette cellule rouge est le côté gauche (le reste est coupé) et l'endroit où elle devrait être est vide.

Cela ne se produit pas systématiquement ; ce n'est pas toujours la même ligne. Une fois que cela s'est produit, je peux faire défiler l'écran vers le haut, puis vers le bas, et la cellule s'est réparée d'elle-même. Ou bien, lorsque j'appuie sur la cellule (ce qui me fait passer à la vue suivante via une pression) et que je reviens en arrière, je verrai la cellule dans une position incorrecte, puis elle passera à la position correcte.

La vitesse de défilement semble faciliter la reproduction du problème. Lorsque je fais défiler le texte lentement, je peux encore voir la cellule dans la mauvaise position de temps en temps, mais elle revient immédiatement à la bonne position.

Le problème a commencé lorsque j'ai ajouté les inserts des sections. Auparavant, les cellules étaient presque au même niveau que les limites de la collection (peu ou pas d'inserts) et je n'avais pas remarqué le problème. Mais cela signifiait que les parties droite et gauche de la vue de la collection étaient vides. En d'autres termes, il était impossible de faire défiler les cellules. De plus, la barre de défilement n'était pas affleurante à droite.

Je peux faire en sorte que le problème se produise à la fois sur le simulateur et sur un iPad 3.

Je suppose que le problème se produit à cause des inserts des sections gauche et droite... Mais si la valeur est erronée, alors je m'attendrais à ce que le comportement soit cohérent. Je me demande si ce n'est pas un bug d'Apple ? Ou peut-être est-ce dû à une accumulation d'inserts ou quelque chose de similaire.

Illustration of problem and settings


Suivi : J'ai utilisé cette réponse sous Nick depuis plus de 2 ans maintenant sans aucun problème (au cas où les gens se demandent s'il y a des trous dans cette réponse - je n'en ai pas encore trouvé). Bien joué Nick.

95voto

Nick Snyder Points 946

Il existe un bogue dans l'implémentation de layoutAttributesForElementsInRect de UICollectionViewFlowLayout qui fait qu'elle renvoie DEUX objets attributs pour une seule cellule dans certains cas impliquant des inserts de section. L'un des objets attributaires renvoyés n'est pas valide (en dehors des limites de la vue collectrice) et l'autre est valide. Vous trouverez ci-dessous une sous-classe de UICollectionViewFlowLayout qui résout le problème en excluant les cellules situées en dehors des limites de la vue de la collection.

// NDCollectionViewFlowLayout.h
@interface NDCollectionViewFlowLayout : UICollectionViewFlowLayout
@end

// NDCollectionViewFlowLayout.m
#import "NDCollectionViewFlowLayout.h"
@implementation NDCollectionViewFlowLayout
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
  NSArray *attributes = [super layoutAttributesForElementsInRect:rect];
  NSMutableArray *newAttributes = [NSMutableArray arrayWithCapacity:attributes.count];
  for (UICollectionViewLayoutAttributes *attribute in attributes) {
    if ((attribute.frame.origin.x + attribute.frame.size.width <= self.collectionViewContentSize.width) &&
        (attribute.frame.origin.y + attribute.frame.size.height <= self.collectionViewContentSize.height)) {
      [newAttributes addObject:attribute];
    }
  }
  return newAttributes;
}
@end

Véase ce .

D'autres réponses suggèrent de renvoyer YES de shouldInvalidateLayoutForBoundsChange, mais cela entraîne des recalculs inutiles et ne résout même pas complètement le problème.

Ma solution résout complètement le bogue et ne devrait pas causer de problèmes lorsque Apple corrigera la cause première.

5 votes

Pour information, ce bogue se produit également avec le défilement horizontal. En remplaçant x par y et width par height, ce patch fonctionne.

0 votes

Merci. Je commençais juste à jouer avec collectionView (si vous voulez spammer Apple à ce sujet, voici la référence rdar openradar.appspot.com/12433891 )

0 votes

Merci Patrick et Yanik. J'ai mis à jour ma réponse pour prendre en charge le défilement vertical également.

8voto

Peter Lapisu Points 3274

Mettez ceci dans le viewController qui possède la vue de la collection

- (void)viewWillLayoutSubviews
{
    [super viewWillLayoutSubviews];
    [self.collectionView.collectionViewLayout invalidateLayout];
}

0 votes

Où est-ce que tu mets ça ?

0 votes

Dans le viewController qui possède la vue de la collection

3 votes

Mon problème était que les cellules ont complètement disparu. Cette solution m'a aidé, mais elle entraîne des rechargements inutiles. Mais cela fonctionne maintenant Merci !

7voto

xxtesaxx Points 170

J'ai découvert des problèmes similaires dans mon application iPhone. En cherchant sur le forum Apple dev, j'ai trouvé cette solution qui a fonctionné dans mon cas et qui fonctionnera probablement aussi dans le vôtre :

Sous-classe UICollectionViewFlowLayout et de passer outre shouldInvalidateLayoutForBoundsChange pour revenir YES .

//.h
@interface MainLayout : UICollectionViewFlowLayout
@end

et

//.m
#import "MainLayout.h"
@implementation MainLayout
-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds{
    return YES;
}
@end

0 votes

Cela ne résout que partiellement le problème que j'ai avec cette question. La cellule se place effectivement au bon endroit lorsque la ligne apparaît. Cependant, juste avant l'apparition de la ligne, une cellule apparaît toujours sur le côté.

0 votes

Attention, si vous faites cela, la mise en page sera exécutée à chaque fois que vous faites défiler la page. Cela peut avoir un impact important sur les performances.

5voto

monowerker Points 2202

J'ai également rencontré ce problème pour une mise en page basique de type gridview avec des insets pour les marges. Le débogage limité que j'ai effectué pour l'instant consiste à mettre en œuvre la méthode suivante - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect dans ma sous-classe UICollectionViewFlowLayout et en enregistrant ce que l'implémentation de la super classe renvoie, ce qui montre clairement le problème.

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
    NSArray *attrsList = [super layoutAttributesForElementsInRect:rect];

    for (UICollectionViewLayoutAttributes *attrs in attrsList) {
        NSLog(@"%f %f", attrs.frame.origin.x, attrs.frame.origin.y);
    }

    return attrsList;
}

En mettant en œuvre - (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath Je peux également voir qu'il semble renvoyer les mauvaises valeurs pour itemIndexPath.item == 30, qui est le facteur 10 du nombre de cellules par ligne de mon gridview, je ne suis pas sûr que cela soit pertinent.

- (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath {
    UICollectionViewLayoutAttributes *attrs = [super initialLayoutAttributesForAppearingItemAtIndexPath:itemIndexPath];

    NSLog(@"initialAttrs: %f %f atIndexPath: %d", attrs.frame.origin.x, attrs.frame.origin.y, itemIndexPath.item);

    return attrs;
}

Comme je n'ai pas le temps de déboguer davantage, la solution que j'ai trouvée pour l'instant consiste à réduire la largeur de mes vues de collection d'une quantité égale aux marges gauche et droite. J'ai un en-tête qui a toujours besoin de la pleine largeur, j'ai donc défini clipsToBounds = NO sur ma collectionview et j'ai également supprimé les insets gauche et droit sur celle-ci, ce qui semble fonctionner. Pour que la vue de l'en-tête reste en place, vous devez implémenter le déplacement et le dimensionnement du cadre dans les méthodes de mise en page chargées de renvoyer les attributs de mise en page de la vue de l'en-tête.

0 votes

Merci pour ces informations supplémentaires @monowerker. Je pense que mon problème a commencé lorsque j'ai ajouté les insets (j'ai ajouté cela à la question). Je vais essayer vos méthodes de débogage et voir si cela me dit quelque chose. Je pourrais aussi essayer votre solution de contournement.

0 votes

Il s'agit très probablement d'un bug dans UICFL/UICL, je vais essayer d'obtenir un radar déposé quand j'aurai le temps, voici une discussion avec quelques numéros rdar que vous pouvez référencer. twitter.com/steipete/status/258323913279410177

4voto

DrMickeyLauer Points 544

J'ai ajouté un rapport de bogue à Apple. Ce qui fonctionne pour moi, c'est de définir la valeur de bottom sectionInset comme étant inférieure à celle de top inset.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X