How can UICollectionView supplementary views be inserted or deleted correctly

强颜欢笑 提交于 2019-12-14 01:25:57

问题


Short question:

Is there a way to add and remove supplementary views like cells and section in a performBatchUpdates: block similar to the insertItemsAtIndexPaths: deleteItemsAtIndexPaths: or even reloadItemsAtIndexPaths: methods?

Long explanation:

I use a UICollectionView with sections. The cells are laid out like a grid and each line can have up to 6 cells. I add additional supplementary views if the total amount of cells do not fill up all lines of a section. The additional supplementary views just fill up the empty space. So the result should be that each section is completely filled with all available cells and the (possible) remaining empty spots are filled with supplementary views. So each line has 6 visual elements, eather a cell or a supplementary view.

To achieve that I implemented a custom UICollectionViewFlowLayout based layout with the following prepareLayout: implementation:

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

This works great if all data is available from the beginning and if there is no change to the data. But it fails miserably if data gets added and removed during the lifetime. Inserts and deletes of cells in a performBatchUpdates: mess up the supplementary views.

What should happen:

For every cell that is inserted with insertItemsAtIndexPaths: the supplementary view at the same indexPath should be removed. Furthermore for every cell that is deleted with deleteItemsAtIndexPaths: a new supplementary view should be added at the same indexPath.

The Problem:

The prepareLayout: of my layout is called and calculates the correct layoutAttributes. But layoutAttributesForSupplementaryViewOfKind:atIndexPath: is called for weird indexPaths. Specifically the layoutAttributesForSupplementaryViewOfKind:atIndexPath: is called for old indexPaths which got removed from the supplementaryViewAttributeList array!

Example:

In the case where a new cell gets inserted, the prepareLayout: correctly removes the layoutAttributes, but the layoutAttributesForSupplementaryViewOfKind:atIndexPath: still gets called for the not longer available and deleted indexPath! And only for that. There is no additional call for any other indexPaths for all the other supplementary views.

Why is layoutAttributesForSupplementaryViewOfKind:atIndexPath: called for indexPaths that are not available any more? How would I delete, insert or reload the supplementary views correctly? What do I miss?

Source:

The full source can be found here:

Custom Layout: https://github.com/sarn/ARNClassicFilms/blob/master/classicFilms/classicFilms/layout/ARNCollectionViewFocusGuideFlowLayout.m

CollectionView Controller: https://github.com/sarn/ARNClassicFilms/blob/master/classicFilms/classicFilms/view-controllers/ARNMovieOverviewController.m (performBatchUpdates: is in the last method of the class)


回答1:


Your comment was not what did the trick for me, i found another bit of code and added it and it worked, and even removed the above suggestion, and it still worked. Collection views are certainly powerful but forget one function such as this one...

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

and things can go fairly awry.




回答2:


One solution is to add a [[collectionView collectionViewLayout] invalidateLayout] to the performBatchUpdates: block. This forces the layout to recalculate and drop the not valid indexPaths.



来源:https://stackoverflow.com/questions/36386854/how-can-uicollectionview-supplementary-views-be-inserted-or-deleted-correctly

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