如何正确插入或删除UICollectionView补充视图

时间:2016-04-03 14:44:47

标签: ios objective-c uicollectionview uicollectionviewlayout uicollectionreusableview

简短问题:

有没有办法在performBatchUpdates:块中添加和删除补充视图,例如单元格和部分,类似于insertItemsAtIndexPaths: deleteItemsAtIndexPaths:甚至reloadItemsAtIndexPaths:方法?

长解释:

我使用UICollectionView部分。细胞布局像网格,每条线最多可以有6个细胞。如果单元格的总量没有填满一个部分的所有行,我会添加额外的补充视图。额外的补充意见只是填补了空白空间。因此,结果应该是每个部分完全填充所有可用的单元格,并且(可能的)剩余的空白点填充有补充视图。因此,每一行都有6个视觉元素,可以收集单元格或补充视图。

为实现这一目标,我使用以下UICollectionViewFlowLayout实现实现了基于prepareLayout:的自定义布局:

- (void)prepareLayout {
    [super prepareLayout];

    self.supplementaryViewAttributeList = [NSMutableArray array];
    if(self.collectionView != nil) {
        // calculate layout values
        CGFloat contentWidth = self.collectionViewContentSize.width - self.sectionInset.left - self.sectionInset.right;
        CGFloat cellSizeWithSpacing = self.itemSize.width + self.minimumInteritemSpacing;
        NSInteger numberOfItemsPerLine = floor(contentWidth / cellSizeWithSpacing);
        CGFloat realInterItemSpacing = (contentWidth - (numberOfItemsPerLine * self.itemSize.width)) / (numberOfItemsPerLine - 1);

        // add supplementary attributes
        for (NSInteger numberOfSection = 0; numberOfSection < self.collectionView.numberOfSections; numberOfSection++) {
            NSInteger numberOfItems = [self.collectionView numberOfItemsInSection:numberOfSection];
            NSInteger numberOfSupplementaryViews = numberOfItemsPerLine - (numberOfItems % numberOfItemsPerLine);

            if (numberOfSupplementaryViews > 0 && numberOfSupplementaryViews < 6) {
                NSIndexPath *indexPathOfLastCellOfSection = [NSIndexPath indexPathForItem:(numberOfItems - 1) inSection:numberOfSection];
                UICollectionViewLayoutAttributes *layoutAttributesOfLastCellOfSection = [self layoutAttributesForItemAtIndexPath:indexPathOfLastCellOfSection];

                for (NSInteger numberOfSupplementor = 0; numberOfSupplementor < numberOfSupplementaryViews; numberOfSupplementor++) {
                    NSIndexPath *indexPathOfSupplementor = [NSIndexPath indexPathForItem:(numberOfItems + numberOfSupplementor) inSection:numberOfSection];
                    UICollectionViewLayoutAttributes *supplementaryLayoutAttributes = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:ARNCollectionElementKindFocusGuide withIndexPath:indexPathOfSupplementor];
                    supplementaryLayoutAttributes.frame = CGRectMake(layoutAttributesOfLastCellOfSection.frame.origin.x + ((numberOfSupplementor + 1) * (self.itemSize.width + realInterItemSpacing)), layoutAttributesOfLastCellOfSection.frame.origin.y, self.itemSize.width, self.itemSize.height);
                    supplementaryLayoutAttributes.zIndex = -1;

                    [self.supplementaryViewAttributeList addObject:supplementaryLayoutAttributes];
                }
            }
        }
    }
}

如果从头开始可以获得所有数据并且数据没有变化,那么效果很好。但是如果在生命周期中添加和删除数据,它就会失败。在performBatchUpdates:中插入和删除单元格会使补充视图陷入混乱。

会发生什么:

对于使用insertItemsAtIndexPaths:插入的每个单元格,应删除同一indexPath处的补充视图。此外,对于使用deleteItemsAtIndexPaths:删除的每个单元格,都应在同一indexPath添加新的补充视图。

问题:

我的布局的prepareLayout:被调用并计算出正确的layoutAttributes。但是layoutAttributesForSupplementaryViewOfKind:atIndexPath:被称为奇怪的indexPaths。具体而言,从layoutAttributesForSupplementaryViewOfKind:atIndexPath:数组中删除的旧indexPaths调用了supplementaryViewAttributeList

示例:

如果插入新单元格,prepareLayout:会正确删除layoutAttributes,但layoutAttributesForSupplementaryViewOfKind:atIndexPath: 仍然会被调用,因为不再可用并删除了indexPath < / STRONG>!仅限于此。对于所有其他补充意见,没有任何其他indexPath的额外呼叫。

为什么layoutAttributesForSupplementaryViewOfKind:atIndexPath:要求indexPaths不再可用?如何正确删除,插入或重新加载补充视图?我错过了什么?

来源:

完整的资料来源可以在这里找到:

自定义布局:https://github.com/sarn/ARNClassicFilms/blob/master/classicFilms/classicFilms/layout/ARNCollectionViewFocusGuideFlowLayout.m

CollectionView控制器: https://github.com/sarn/ARNClassicFilms/blob/master/classicFilms/classicFilms/view-controllers/ARNMovieOverviewController.mperformBatchUpdates:位于班级的最后一个方法中)

2 个答案:

答案 0 :(得分:2)

您的评论不是我的诀窍,我找到了另一部分代码并添加了它并且它有效,甚至删除了上述建议,它仍然有效。集合视图肯定很强大,但忘记了一个这样的功能...

- (NSArray<NSIndexPath *>   *)indexPathsToDeleteForSupplementaryViewOfKind:(NSString *)elementKind
{
    return self.removedIndexPaths;
}

事情可能会非常糟糕。

答案 1 :(得分:1)

一种解决方案是向[[collectionView collectionViewLayout] invalidateLayout]块添加performBatchUpdates:。这会强制布局重新计算并删除无效的indexPaths。