Um bug do UICollectionView com uma altura dinâmica
Como eu estou lutando há 3 dias com esse problema e já o perguntei duas vezes, mas talvez não estivesse claro, decidi investigar o problema e encontrei um comportamento de buggy com essa visão.
Vou mostrar todo o código simples, para que qualquer pessoa possa reproduzir o bug (iPad Air).
Estou definindo umcollectionView
layout de fluxo que subclasse o layout para obter um espaçamento constante entre as células, e aqui está o começo:
TopAlignedCollectionViewFlowLayout *layout = [[TopAlignedCollectionViewFlowLayout alloc] init];
CGRect size = CGRectMake(0, 0, 900, 1200);
self.GridView = [[UICollectionView alloc] initWithFrame:size
collectionViewLayout:layout];
[self.GridView registerClass:[GridCell class] forCellWithReuseIdentifier:@"Cell"];
[self.GridView setDelegate:self];
[self.GridView setDataSource:self];
[self.view addSubview:self.GridView];
Em seguida, definir meus delegados é tão simples quanto isso: (altura é dinâmica )
#pragma grid- main functions
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return 1;
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return 80;
}
//cell size
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout
, sizeForItemAtIndexPath:(NSIndexPath *)indexPath;
{
//a random dynamic height of a cell
int a = arc4random()%300;
CGSize size = CGSizeMake( 340, 240+a );
return size;
}
-(UICollectionViewCell*)collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier=@"Cell";
GridCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier
forIndexPath:indexPath];
cell.textL.text=[NSString stringWithFormat:@"%d",indexPath.row];
NSLog(@"%d",indexPath.row);
return cell;
}
Agora a subclasse, para obter um espaçamento constante: (TopAlignedCollectionViewFlowLayout
)
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
NSArray* attributesToReturn = [super layoutAttributesForElementsInRect:rect];
for (UICollectionViewLayoutAttributes* attributes in attributesToReturn) {
if (nil == attributes.representedElementKind) {
NSIndexPath* indexPath = attributes.indexPath;
attributes.frame = [self layoutAttributesForItemAtIndexPath:indexPath].frame;
}
}
return attributesToReturn;
}
#define numColumns 2
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewLayoutAttributes* currentItemAttributes = [super layoutAttributesForItemAtIndexPath:indexPath];
if (indexPath.item < numColumns) {
CGRect f = currentItemAttributes.frame;
f.origin.y = 0;
currentItemAttributes.frame = f;
return currentItemAttributes;
}
NSIndexPath* ipPrev = [NSIndexPath indexPathForItem:indexPath.item-numColumns
inSection:indexPath.section];
CGRect fPrev = [self layoutAttributesForItemAtIndexPath:ipPrev].frame;
CGFloat YPointNew = fPrev.origin.y + fPrev.size.height + 10;
CGRect f = currentItemAttributes.frame;
f.origin.y = YPointNew;
currentItemAttributes.frame = f;
return currentItemAttributes;
}
Qualquer um pode verificar e ver que, depois de rolar por um tempo, você obtém um efeito estranho de espaços em branco que são preenchidos ultimamente por suas células, algo como:
1 2
3 4
6
8
NOTA: 5-7 são carregados mais tarde.
EDIT1:
A remoção da altura aleatória do método de delegação do tamanho da célula, defina-a como altura constante, resolve esse problema.
O problema é: a altura da célula deve ser dinâmica.
EDIT2: Definir a altura aleatória (int a) como menor também faz com que o problema desapareça (<100), significa que quanto menor a altura da distância entre as células, mais provavelmente o problema não ocorrerá.
EDIT3!
Consegui definir uma distância constante entre as células, não com a subclasse do layout, mas com minha própria memória, salvando a origem e a altura da célula anterior,então eu tenho o espaçamento constante, mas o problema está de volta ! Parece que, se as células estiverem em alguma estrutura, faz com que o método de retorno de chamada que cria as células não seja chamado a tempo! uau, eu estou realmente querendo saber como ninguém tinha visto isso antes .. aqui está a minha implementação para criar espaçamento sem subclassificação, que também causam o problema:
-(UICollectionViewCell*)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier=@"Cell";
GridCell *cell=[collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
cell.textL.text=[NSString stringWithFormat:@"%ld",(long)indexPath.row];
NSLog(@"%d",indexPath.row);
if(indexPath.row>1)
{
NSIndexPath* ipPrev = [NSIndexPath indexPathForItem:indexPath.item-2 inSection:indexPath.section];
float prey=[[[NSUserDefaults standardUserDefaults] objectForKey:[NSString stringWithFormat:@"y:%ld",(long)ipPrev.row]] floatValue];
float preh=[[[NSUserDefaults standardUserDefaults] objectForKey:[NSString stringWithFormat:@"h:%ld",(long)ipPrev.row]] floatValue];
cell.frame=CGRectMake(cell.frame.origin.x, preh+prey+10, cell.frame.size.width, cell.frame.size.height);
[[NSUserDefaults standardUserDefaults] setFloat:cell.frame.origin.y forKey:[NSString stringWithFormat:@"y:%ld",(long)indexPath.row]];
[[NSUserDefaults standardUserDefaults] setFloat:cell.frame.size.height forKey:[NSString stringWithFormat:@"h:%ld",(long)indexPath.row]];
[[NSUserDefaults standardUserDefaults] synchronize];
NSLog(@"this index:%d",indexPath.row);
NSLog(@"this cell y:%f",cell.frame.origin.y);
NSLog(@"this cell height:%f",cell.frame.size.height);
NSLog(@"previous index:%ld",(long)ipPrev.row);
NSLog(@"previous cell y: %@",[[NSUserDefaults standardUserDefaults] objectForKey:[NSString stringWithFormat:@"y:%ld",(long)ipPrev.row]]);
NSLog(@"previous cell height: %@",[[NSUserDefaults standardUserDefaults] objectForKey:[NSString stringWithFormat:@"h:%ld",(long)ipPrev.row]]);
NSLog(@"------------------------");
}
return cell;
}