【问题标题】:Issues inserting into UICollectionView section which contains a footer插入包含页脚的 UICollectionView 部分的问题
【发布时间】:2014-08-02 04:35:33
【问题描述】:

我有一个典型的 UICollectionView,它以垂直方式使用 UICollectionViewFlowLayout。我正在使用带有分页功能的 rest API 来填充集合视图。为了触发下一页下载,我在请求页脚布局时使用了委托:

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

这是我的 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];
    }];
}

当我运行时,我可以转到我的 ViewController 并查看加载的资产的第一页。如果我向下滚动,我会看到页脚视图(其中包含一个微调器),但随后代码将在异常断点处中断:

[weakSelf.collectionView insertItemsAtIndexPaths:indexPaths];

-[UICollectionViewData layoutAttributesForSupplementaryElementOfKind:atIndexPath:] 中的断言失败,/SourceCache/UIKit/UIKit-3185.20/UICollectionViewData.m:829


然后,如果我继续,它会因错误而崩溃:

2014-06-11 16:39:58.335 Radius-iOS[4901:525006] * 由于未捕获的异常“NSInternalInconsistencyException”而终止应用程序,原因:“-layoutAttributesForSupplementaryElementOfKind 没有 UICollectionViewLayoutAttributes 实例:UICollectionElementKindSectionFooter 在路径 {length = 2,路径 = 0 - 0}' * 首先抛出调用栈:


这让我很困惑。 layoutAttributesForSupplementaryElementOfKind 听起来好像与布局类有关,但我没有使用自定义流布局,而是提供的默认布局。听起来 Apple 的代码很疯狂,但我觉得我使用的一切都是正确的。

现在如果我移动电话:

[self loadNextPageOfAssets];

从补充单元 dequeue 到 UICollectionViewCell deque,并一起删除页脚视图,然后插入效果很好。

现在我正在调用 reloadData 而不是 insert,但这很丑陋。

我是否忽略了页脚的某些内容?

【问题讨论】:

  • wel.. 对于初学者,您可以检查传递给 viewForSupplementaryElementOfKind 的补充元素种类是什么

标签: ios uicollectionview uicollectionviewlayout uicollectionreusableview


【解决方案1】:

有一篇关于 UICollectionView 中的错误的俄语文章:http://habrahabr.ru/post/211144/

该文章中的几种方法可以解决您的问题:

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

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

【讨论】:

    【解决方案2】:

    您应该同时实现(页眉和页脚)方法。即使你只想要一个。看我的代码

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

    【讨论】:

      【解决方案3】:

      事实证明,我实际上遇到了布局问题(如错误描述所示)

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

      CGSizeZero 是导致崩溃的原因。而是使用 CGSizeMake(0.001, 0.001)。这是唯一必要的改变。它现在按预期运行。

      【讨论】:

      • 谢谢!有什么理由不能简单地返回 CGSizeZero?必须处理集合视图的发脾气非常烦人:(
      • 这非常令人沮丧,但是这对它进行了排序,谢谢! :)
      【解决方案4】:

      VaporwareWolf 提供的答案有点骇人听闻。通过返回小尺寸而不是零尺寸,补充视图将始终存在,但尺寸太小而无法看到。这就是它修复 NSInternalInconsistencyException

      的原因

      但是,有一个真正的解决方案

      之后将数据添加到数据源并 在调用insertItemsAtIndexPaths 之前,只需使collectionview 上的布局无效以使其了解更改。

      Objective-C

      [self.collectionView.collectionViewLayout invalidateLayout]
      

      斯威夫特

      collectionView.collectionViewLayout.invalidateLayout()
      

      【讨论】:

      • 我同意你的看法。自从几年前发表这篇文章以来,我已经学到了很多关于 UICollectionView 的知识。将此更改为已接受的答案。
      • 感谢您的出色回答。我也面临同样的问题。当数据源为空时,使用默认的自定义布局并为我的页脚视图返回 CGSizeZero。如果我在插入或移动方法之前不调用 invalidateLayout,应用程序将崩溃
      猜你喜欢
      • 1970-01-01
      • 2015-04-03
      • 2011-09-23
      • 1970-01-01
      • 2017-02-18
      • 2020-12-22
      • 1970-01-01
      • 2017-07-31
      • 1970-01-01
      相关资源
      最近更新 更多