Issues inserting into UICollectionView section which contains a footer

梦想与她 提交于 2019-12-03 17:43:43

问题


I've got a typical UICollectionView which is using UICollectionViewFlowLayout in a vertical fashion. I'm using a rest API with pagination to populate the collection view. In order to trigger the next page to download, I'm using the delegate when it asks for the footer layout:

- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath{
    RDLoadMoreReusableView *view = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:@"RDLoadMoreReusableView" forIndexPath:indexPath];
    view.delegate = self;
    [view start];
    [self loadNextPageOfAssets];
    return view;
}

Here is the code behind my loadNextPageOfAssets:

-(void)loadNextPageOfAssets{
    __weak RDRadiusGridViewController *weakSelf = self;

    // Increment page and request assets. They are added to cluster.assets before the completion block is called.
    self.cluster.pagination.page++;
    [self.cluster requestAssetsWithCompletionBlock:^(NSArray *assets) {

        SM_LOG_DEBUG(@"page.total: %ld assets.count: %ld", self.cluster.pagination.totalCount, (long)assets.count);

        NSMutableArray *indexPaths = [[NSMutableArray alloc]initWithCapacity:assets.count];
        for(NSUInteger index = 0; index < assets.count; index++){
            NSIndexPath *indexPath = [NSIndexPath indexPathForItem:self.cluster.assets.count - assets.count + index inSection:0];
            SM_LOG_DEBUG(@"Inserting indexPath: %ld:%ld", (long)indexPath.item, (long)indexPath.section);
            [indexPaths addObject:indexPath];
        }
        [weakSelf.collectionView performBatchUpdates:^{
            [weakSelf.collectionView insertItemsAtIndexPaths:indexPaths];
        } completion:^(BOOL finished) {

        }];

//            [weakSelf.collectionView reloadData];
    }];
}

When I run I can go to my ViewController and see the first page of assets loaded. If I scroll down I'll see the footer view (which contains a spinner), but then the code will break at the exception break point at line:

[weakSelf.collectionView insertItemsAtIndexPaths:indexPaths];

Assertion failure in -[UICollectionViewData layoutAttributesForSupplementaryElementOfKind:atIndexPath:], /SourceCache/UIKit/UIKit-3185.20/UICollectionViewData.m:829


Then if I continue it crashes with the error:

2014-06-11 16:39:58.335 Radius-iOS[4901:525006] * Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'no UICollectionViewLayoutAttributes instance for -layoutAttributesForSupplementaryElementOfKind: UICollectionElementKindSectionFooter at path {length = 2, path = 0 - 0}' * First throw call stack:


This is puzzling to me. layoutAttributesForSupplementaryElementOfKind sounds like it's seomthing to do with the layout class, but I'm not using a custom flow layout rather the default one supplied. It sounds like Apple's code is mad but I feel that I'm using everything correctly.

Now if I move the call:

[self loadNextPageOfAssets];

from the supplementary cell dequeue to teh UICollectionViewCell deque, and remove the footer views all together, then the insert works great.

For now I'm calling reloadData instead of insert, but this is UGLY.

Am I overlooking something about the footers?


回答1:


The answer provided by VaporwareWolf is a bit of a hack. By returning a tiny size instead of a zero size, the supplementary view will always exist but at a size too small to see. So that's why it fixes the NSInternalInconsistencyException

But, there is a real solution.

After adding the data to the datasource and before calling insertItemsAtIndexPaths, just invalidate the layout on the collectionview to make it aware of the changes.

Objective-C

[self.collectionView.collectionViewLayout invalidateLayout]

Swift

collectionView.collectionViewLayout.invalidateLayout()



回答2:


It turns out that I was actually experiencing a problem with layout (as the error description suggests)

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section{
    if(<myDecision>){
        return CGSizeZero;
    } else {
        return CGSizeMake(320, 50);
    }
}

CGSizeZero is what was causing the crash. Instead use CGSizeMake(0.001, 0.001). That's the only change that was necessary. It runs as intended now.




回答3:


There is a huge article in Russian about bugs in UICollectionView: http://habrahabr.ru/post/211144/.

Here are a couple of methods from that article that may solve your problem:

@try {
    [self.collectionView insertItemsAtIndexPaths:indexPaths];
}
@catch (NSException *exception) {}

or

[self.collectionView performBatchUpdates:^{
        [self.collectionView reloadData];
    } completion:nil];



回答4:


You should implement both (Header and Footer) methods. Even if you want only one. Look at my code

-(UICollectionReusableView *) collectionView:(UICollectionView *) collectionView
           viewForSupplementaryElementOfKind:(NSString *) kind
                                 atIndexPath:(NSIndexPath *) indexPath
{
    UICollectionReusableView *header = [[UICollectionReusableView alloc] init];

    if ([kind isEqualToString:UICollectionElementKindSectionHeader]) {

        header = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"CollectionViewHeader" forIndexPath:indexPath];
    }
    else {
        header = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionFooter withReuseIdentifier:@"CollectionViewHeader" forIndexPath:indexPath];
    }

    return header;
}

-(CGSize) collectionView:(UICollectionView *) collectionView
                         layout:(UICollectionViewLayout *) collectionViewLayout
referenceSizeForHeaderInSection:(NSInteger) section
{
    return CGSizeMake(self.view.frame.size.width, 100);
}

-(CGSize) collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section {
    return CGSizeZero;
}


来源:https://stackoverflow.com/questions/24174456/issues-inserting-into-uicollectionview-section-which-contains-a-footer

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!