A UICollectionView bug with a dynamic height

2019-02-15 22:04发布

问题:

Since I have been struggling for 3 days with this problem and have asked about it twice already, but maybe was not clear, I had decided to investigate the issue & found a buggy behavior with this view.

I will show the entire simple code, so anyone can reproduce the bug (iPad Air).

I am setting a collectionView flowlayout that subclasses the layout to get a constant spacing between cells, and here is the start:

 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];

Then setting my delegates is as simple as that : (height is dynamic )

#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;
}

Now the subclass, to get a constant spacing : (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;
}

Anyone can check and see that after you scroll for a while, you get a strange effect of blank spaces that are filled lately by their cells,something like :

 1 2
 3 4
   6
   8

NOTE: 5-7 are loaded in later.


EDIT1:

Removing the random height from the cell size delegate method, set it to be constant height, solves this issue.
Problem is: Cell's height must be dynamic.

EDIT2: Setting the random height (int a) to be smaller, makes also the problem to disappear,(<100), means that the smaller the distance height between cells, more likely the problem will not occur .

EDIT3 !

I have managed to set a constant distance between cells, not with subclass of the layout, but with my own memory by saving the previous cell origin and height, so i have got the constant spacing but the problem is back again ! seems that if the cells are in some certain structure, it makes the callback method that create cells, to not being called in time ! wow , i am really wondering how no one had seen this before .. here is my implementation to create spacing with no subclassing,that also cause the problem:

-(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;

}

回答1:

Seems its a serious bug in UICollectionView, when images are bigger than screen size.

1 . UICollectionView fall back to UIScrollView

2.Large UICollectionViewCell's disappearing with custom layout

3.http://stripysock.com.au/blog/2013/3/14/working-around-a-bug-in-uicollectionview

So much work and time, for this stupid bug of apple with the reusable cells that causes nothing but headache with so many strange behaviours.

For developers who didn't have the problem i can say- just try to set dynamic height images, and iPad simulator, and you will see bugs that are just unbelievable.

So no answer for me, i will have to implement the whole thing by my self with a scrollview ,since i dont want to be depended again on things such PSTCollectionView