La grande UICollectionViewCell a cessé d'être affichée lors du défilement

Le même comportement de UICollectionView que celui décrit ici a été conduit à cette question . Même si j'ai décidé de postr le mien, parce que j'ai fait d'autres investigations, je n'ai pas voulu postr dans un commentaire ou dans le assembly de la question mentionnée ci-dessus.

Ce qui se produit?:

Lorsque les grandes cellules sont affichées dans un UICollectionView avec un UICollectionViewFlowLayout , après le défilement de la vue de collection à un certain décalage, les cellules disparaissent.

Lorsque vous continuez à défiler jusqu'à ce qu'une autre cellule arrive dans la zone visible, la cellule disparue / cachée redevient visible.

J'ai testé avec une vue de collection de défilement vertical et des cellules pleine largeur, mais je suis plutôt sûr que cela se produirait aussi avec des configurations similaires pour le défilement horizontal.

Quelles sont les grandes cellules ?:

Le comportement décrit se produit avec des cellules supérieures à deux fois la hauteur d'affichage ( 960.f + 1.f sur les écrans 3,5 pouces, 1136.f + 1.f sur 4 pouces).

Que se passe-t-il exactement ?:

Lorsque le décalage de défilement de la vue de collection dépasse cell.frame.origin.y + displayHeightOfHardware la propriété cachée des cellules est définie sur YES et -collectionView:didEndDisplayingCell:forItemAtIndexPath: est appelée (par exemple, la première cellule est masquée lorsque scrollingOffset.y atteint 481.f sur l'iPhone de 3,5 pouces).

Comme décrit ci-dessus, lorsque vous faites défiler jusqu'à la cellule suivante, la cellule masquée s'affiche à nouveau (la propriété masquée passe à NO ) et de plus, lorsque vous faites défiler assez loin, la cellule ne disparaîtra plus jamais. vous faites défiler jusqu'à.

Cela change lorsque vous travaillez avec des cellules plus grandes que la sortingple hauteur d'affichage ( 1441.f/1705.f ). Ceux-ci montrent le même comportement, mais il rest le même, peu importe à quelle distance ils sont défilés de haut en bas.

Quoi d'autre?:

La situation ne peut pas être -(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds pour returnner YES .

Les cellules ne peuvent pas être forcées à afficher en définissant la propriété cachée à NO programmation après qu'elles ont été cachées (dans didEndDisplayingCell par exemple)

Alors, quelle est la question ?:

Je suis assez sûr, que c'est un bug dans UICollectionView/Controller/Cell/Layout et je vais soumettre un TSI chez Apple. Mais pour l'instant: Quelqu'un at-il des idées pour une solution de piratage rapide ?

J'ai une solution très sale et interne pour ce problème:

 @interface UICollectionView () - (CGRect)_visibleBounds; @end @interface MyCollectionView : UICollectionView @end @implementation MyCollectionView - (CGRect)_visibleBounds { CGRect rect = [super _visibleBounds]; rect.size.height = [self heightOfLargestVisibleCell]; return rect; } - (float)heightOfLargestVisibleCell { // do your calculations for current max cellHeight and return it return 1234; } @end 

J'ai une solution de contournement qui semble fonctionner pour moi et ne devrait pas courir fou de règles d'Apple pour les applications iOS.

La key est l'observation que les grandes limites de cellules sont le problème. J'ai travaillé autour de cela en veillant à ce qu'un bord de la cellule se trouve dans la zone visible de la région de contenu déroulable. Vous devrez évidemment sous-classr la class UICollectionViewFlowLayout ou UICollectionViewLayout en fonction de vos besoins et utiliser la valeur contentOffset pour suivre votre position dans UIScrollView.

Je devais aussi assurer:

 - (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds 

renvoie YES ou fait face à une exception d'exécution indiquant que la disposition n'était pas valide. Je garde le bord de la plus grosse cellule lié au bord gauche dans mon cas. De cette façon, vous pouvez éviter la détection des intersections de limites erronées pour ces cellules plus grandes.

Cela crée plus de travail en fonction de la façon dont vous souhaitez que le contenu de la cellule soit rendu que la largeur / hauteur de la cellule est mise à jour lorsque vous faites défiler. Dans mon cas, les sous-vues au sein de la cellule sont relativement simples et ne nécessitent pas beaucoup de manipulation.

Comme demandé ici est un exemple de mon layoutAtsortingbutesInRect

 - (NSArray *)layoutAtsortingbutesForElementsInRect:(CGRect)rect { NSMutableArray* atsortingbutes = [NSMutableArray array]; NSArray *vertical = myVerticalCellsStore.cells; NSInteger startRow = floor(rect.origin.y * (vertical.count)/ (vertical.count * verticalViewHeight + verticalViewSpacing * 2)); startRow = (startRow < 0) ? 0 : startRow; for (NSInteger i = startRow; i < vertical.count && (rect.origin.y + rect.size.height >= i * verticalViewHeight); i++) { NSArray *horizontals = myHorizontalStore.horizontalCells; UICollectionViewLayoutAtsortingbutes *verticalAttr = [self layoutAtsortingbutesForSupplementaryViewOfKind:@"vertical" atIndexPath:[NSIndexPath indexPathForItem:0 inSection:i]]; if (CGRectIntersectsRect(verticalAttr.frame, rect)) { [atsortingbutes addObject:verticalAttr]; } BOOL foundAnElement = NO; for (NSInteger j = 0 ; j < horizontals.count; j++) { MYViewLayoutAtsortingbutes *attr = (MyViewLayoutAtsortingbutes *)[self layoutAtsortingbutesForItemAtIndexPath:[NSIndexPath indexPathForItem:j inSection:i]]; if (CGRectIntersectsRect(rect, attr.frame)) { [atsortingbutes addObject: attr]; foundAnElement = YES; } else if (foundAnElement) { break; } } } return atsortingbutes; } 

C'est mon code aseptisé. Fondamentalement, je calcule à peu près la première cellule devrait être basée sur la hauteur de la cellule. Dans mon cas c'est corrigé, donc le calcul est assez facile. Mais mes éléments horizontaux ont des largeurs différentes. Donc, la boucle interne consiste vraiment à déterminer le bon nombre de cellules horizontales à inclure dans le tableau d'attributes. J'utilise le CGRectIntersectsRect pour déterminer si la cellule se croise. Ensuite, la boucle continue jusqu'à ce que l'intersection échoue. Et si au less une cellule horizontale a été trouvée, la boucle va se rompre. J'espère que cela pourra aider.

Ma solution est fondamentalement la même que celle de Jonathan, mais dans une catégorie, vous n'avez donc pas besoin d'utiliser votre propre sous-class.

 @implementation UICollectionView (MTDFixDisappearingCellBug) + (void)load { NSError *error = nil; NSSsortingng *visibleBoundsSelector = [NSSsortingng ssortingngWithFormat:@"%@isib%@unds", @"_v",@"leBo"]; if (![[self class] swizzleMethod:NSSelectorFromSsortingng(visibleBoundsSelector) withMethod:@selector(mtd_visibleBounds) error:&error]) { FKLogErrorVariables(error); } } - (CGRect)mtd_visibleBounds { CGRect bounds = [self mtd_visibleBounds]; // swizzled, no infinite loop MTDDiscussCollectionViewLayout *layout = [MTDDiscussCollectionViewLayout castedObjectOrNil:self.collectionViewLayout]; // Don`t ask me why, but there's a visual glitch when the collection view is scrolled to the top and the max height is too big, // this fixes it if (bounds.origin.y <= 0.f) { return bounds; } bounds.size.height = MAX(bounds.size.height, layout.maxColumnHeight); return bounds; } @end 

J'ai découvert que ce problème ne se produisait que lorsque UICollectionViewLayoutAtsortingbutes était sous-classé et que cette class d'atsortingbut n'avait pas de méthode isEqual: correcte.

Donc par exemple:

 @implementation COGridCollectionViewLayoutAtsortingbutes - (id)copyWithZone:(NSZone *)zone { COGridCollectionViewLayoutAtsortingbutes *atsortingbutes = [super copyWithZone:zone]; atsortingbutes.isInEditMode = _isInEditMode; return atsortingbutes; } - (BOOL)isEqual:(id)other { if (other == self) { return YES; } if (!other || ![[other class] isEqual:[self class]]) { return NO; } if ([((COGridCollectionViewLayoutAtsortingbutes *) other) isInEditMode] != [self isInEditMode]) { return NO; } return [super isEqual:other]; } @end 

J'ai travaillé mais à l'origine j'avais:

 return YES; 

C'est sur iOS 7.