How to make Supplementary View float in UICollectionView as Section Headers do in UITableView plain style

后端 未结 13 725
逝去的感伤
逝去的感伤 2020-11-27 09:35

I\'m struggling to achieve a \"floating section header\" effect with UICollectionView. Something that\'s been easy enough in UITableView (default b

13条回答
  •  被撕碎了的回忆
    2020-11-27 10:22

    Either implement the following delegate methods:

    – collectionView:layout:sizeForItemAtIndexPath:
    – collectionView:layout:insetForSectionAtIndex:
    – collectionView:layout:minimumLineSpacingForSectionAtIndex:
    – collectionView:layout:minimumInteritemSpacingForSectionAtIndex:
    – collectionView:layout:referenceSizeForHeaderInSection:
    – collectionView:layout:referenceSizeForFooterInSection:
    

    In your view controller that has your :cellForItemAtIndexPath method (just return the correct values). Or, instead of using the delegate methods, you may also set these values directly in your layout object, e.g. [layout setItemSize:size];.

    Using either of these methods will enable you to set your settings in Code rather than IB as they're removed when you set a Custom Layout. Remember to add to your .h file, too!

    Create a new Subclass of UICollectionViewFlowLayout, call it whatever you want, and make sure the H file has:

    #import 
    
    @interface YourSubclassNameHere : UICollectionViewFlowLayout
    
    @end
    

    Inside the Implementation File make sure it has the following:

    - (NSArray *) layoutAttributesForElementsInRect:(CGRect)rect {
    
        NSMutableArray *answer = [[super layoutAttributesForElementsInRect:rect] mutableCopy];
        UICollectionView * const cv = self.collectionView;
        CGPoint const contentOffset = cv.contentOffset;
    
        NSMutableIndexSet *missingSections = [NSMutableIndexSet indexSet];
        for (UICollectionViewLayoutAttributes *layoutAttributes in answer) {
            if (layoutAttributes.representedElementCategory == UICollectionElementCategoryCell) {
                [missingSections addIndex:layoutAttributes.indexPath.section];
            }
        }
        for (UICollectionViewLayoutAttributes *layoutAttributes in answer) {
            if ([layoutAttributes.representedElementKind isEqualToString:UICollectionElementKindSectionHeader]) {
                [missingSections removeIndex:layoutAttributes.indexPath.section];
            }
        }
    
        [missingSections enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
    
            NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:idx];
    
            UICollectionViewLayoutAttributes *layoutAttributes = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader atIndexPath:indexPath];
    
            [answer addObject:layoutAttributes];
    
        }];
    
        for (UICollectionViewLayoutAttributes *layoutAttributes in answer) {
    
            if ([layoutAttributes.representedElementKind isEqualToString:UICollectionElementKindSectionHeader]) {
    
                NSInteger section = layoutAttributes.indexPath.section;
                NSInteger numberOfItemsInSection = [cv numberOfItemsInSection:section];
    
                NSIndexPath *firstCellIndexPath = [NSIndexPath indexPathForItem:0 inSection:section];
                NSIndexPath *lastCellIndexPath = [NSIndexPath indexPathForItem:MAX(0, (numberOfItemsInSection - 1)) inSection:section];
    
                NSIndexPath *firstObjectIndexPath = [NSIndexPath indexPathForItem:0 inSection:section];
                NSIndexPath *lastObjectIndexPath = [NSIndexPath indexPathForItem:MAX(0, (numberOfItemsInSection - 1)) inSection:section];
    
                UICollectionViewLayoutAttributes *firstObjectAttrs;
                UICollectionViewLayoutAttributes *lastObjectAttrs;
    
                if (numberOfItemsInSection > 0) {
                    firstObjectAttrs = [self layoutAttributesForItemAtIndexPath:firstObjectIndexPath];
                    lastObjectAttrs = [self layoutAttributesForItemAtIndexPath:lastObjectIndexPath];
                } else {
                    firstObjectAttrs = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader
                                                                        atIndexPath:firstObjectIndexPath];
                    lastObjectAttrs = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionFooter
                                                                       atIndexPath:lastObjectIndexPath];
                }
    
                CGFloat headerHeight = CGRectGetHeight(layoutAttributes.frame);
                CGPoint origin = layoutAttributes.frame.origin;
                origin.y = MIN(
                               MAX(
                                   contentOffset.y + cv.contentInset.top,
                                   (CGRectGetMinY(firstObjectAttrs.frame) - headerHeight)
                                   ),
                               (CGRectGetMaxY(lastObjectAttrs.frame) - headerHeight)
                               );
    
                layoutAttributes.zIndex = 1024;
                layoutAttributes.frame = (CGRect){
                    .origin = origin,
                    .size = layoutAttributes.frame.size
                };
    
            }
    
        }
    
        return answer;
    
    }
    
    - (BOOL) shouldInvalidateLayoutForBoundsChange:(CGRect)newBound {
    
        return YES;
    
    }
    

    Choose "Custom" in Interface Builder for the Flow Layout, choose your "YourSubclassNameHere" Class that you just created. And Run!

    (Note: the code above may not respect contentInset.bottom values, or especially large or small footer objects, or collections that have 0 objects but no footer.)

提交回复
热议问题