JJCollectionViewRoundFlowLayout Custom Section Header and Footer Calculation

JJCollectionViewRoundFlowLayout calculates section header and footer frames by querying supplementary view attributes, merging them with cell frames using CGRectUnion, and applying custom border insets to generate rounded background decoration views.

The kingjiajie/jjcollectionviewroundflowlayout repository extends UICollectionViewFlowLayout to draw rounded backgrounds behind collection view sections. Understanding JJCollectionViewRoundFlowLayout custom section header and footer calculation is essential for creating polished section backgrounds that properly encompass headers, footers, and cells while handling edge cases like empty sections and irregular cell sizes.

Collecting Section Information

In JJCollectionViewRoundFlowLayout.m, the ‑prepareLayout method iterates over every section using a standard for loop: for (NSInteger section = 0; section < sections; section++). For each section, the layout determines geometry by first obtaining the first and last item frames via layoutAttributesForItemAtIndexPath:. If the section is empty, the delegate method collectionView:layout:isCanCalculateWhenRowEmptyWithSection: (or the property isCanCalculateWhenRowEmpty) determines whether processing continues.

Header Detection and Frame Calculation

The layout first evaluates the isCalculateHeader property. However, the delegate can override this per section:

BOOL isCalculateHeaderView = self.isCalculateHeader;
if ([delegate respondsToSelector:@selector(collectionView:layout:isCalculateHeaderViewIndex:)]) {
    isCalculateHeaderView = [delegate collectionView:self.collectionView layout:self isCalculateHeaderViewIndex:section];
}

When isCalculateHeaderView is YES, the layout fetches attributes via layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader. If a header exists, its frame becomes the firstFrame; otherwise, the layout uses the first cell's frame. For sections with irregularly sized cells, enabling isCalculateTypeOpenIrregularitiesCell triggers calculateIrregularitiesCellByMinTopFrameWithLayout: to ensure the background covers all cells regardless of size variations.

The footer follows an identical pattern using isCalculateFooter, collectionView:layout:isCalculateFooterViewIndex:, and layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionFooter. The resulting footer frame serves as the lastFrame, or the layout falls back to the last cell. When handling irregular layouts, calculateIrregularitiesCellByMaxBottomFrameWithLayout: expands the frame to include the bottom-most cells.

Merging Frames and Applying Insets

The layout unites the header and footer frames using CGRectUnion(firstFrame, lastFrame) to create the section content rectangle. It then applies two types of insets:

  • Standard section insets from JJCollectionViewFlowLayoutUtils evaluatedSectionInsetForItemWithCollectionLayout:
  • Custom border insets provided by the delegate via collectionView:layout:borderEdgeInsertsForSectionAtIndex:

These insets are added to the calculated rectangle and stored in the decoration attributes' borderEdgeInsets property. The final JJCollectionViewRoundLayoutAttributes object receives the section frame, zIndex of -1 (to appear behind cells), and a myConfigModel instance containing visual styling parameters.

JJCollectionViewRoundLayoutAttributes *attr = [JJCollectionViewRoundLayoutAttributes
    layoutAttributesForDecorationViewOfKind:JJCollectionViewRoundSection
    withIndexPath:[NSIndexPath indexPathForRow:0 inSection:section]];
attr.frame = sectionFrame;
attr.zIndex = -1;
attr.borderEdgeInsets = userCustomSectionInset;
attr.myConfigModel = [delegate collectionView:self.collectionView layout:self configModelForSectionAtIndex:section];

Implementing the Delegate Protocol

To customize header and footer calculation behavior, implement the JJCollectionViewDelegateRoundFlowLayout protocol defined in JJCollectionViewRoundFlowLayout.h.

Basic Setup

#import "JJCollectionViewRoundFlowLayout.h"

- (void)viewDidLoad {
    [super viewDidLoad];

    JJCollectionViewRoundFlowLayout *layout = [JJCollectionViewRoundFlowLayout new];
    layout.isRoundEnabled = YES;
    layout.isCalculateHeader = YES;
    layout.isCalculateFooter = YES;
    layout.delegate = self;
    self.collectionView.collectionViewLayout = layout;
}

Override default calculation flags for specific sections:

- (BOOL)collectionView:(UICollectionView *)collectionView
               layout:(UICollectionViewLayout *)collectionViewLayout
isCalculateHeaderViewIndex:(NSInteger)section {
    // Only even-indexed sections calculate header backgrounds
    return (section % 2 == 0);
}

- (BOOL)collectionView:(UICollectionView *)collectionView
               layout:(UICollectionViewLayout *)collectionViewLayout
isCalculateFooterViewIndex:(NSInteger)section {
    return YES;
}

Providing Visual Configuration

Supply styling and custom border insets:

- (JJCollectionViewRoundConfigModel *)collectionView:(UICollectionView *)collectionView
                                            layout:(UICollectionViewLayout *)collectionViewLayout
                         configModelForSectionAtIndex:(NSInteger)section {
    JJCollectionViewRoundConfigModel *model = [JJCollectionViewRoundConfigModel new];
    model.backgroundColor = [UIColor colorWithWhite:0.95 alpha:1.0];
    model.cornerRadius = 12.0;
    model.borderWidth = 1.0;
    model.borderColor = [UIColor lightGrayColor];
    model.shadowColor = [UIColor blackColor];
    model.shadowOpacity = 0.2;
    model.shadowRadius = 4.0;
    return model;
}

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView
                        layout:(UICollectionViewLayout *)collectionViewLayout
            borderEdgeInsertsForSectionAtIndex:(NSInteger)section {
    return UIEdgeInsetsMake(5, 10, 5, 10);
}

Handling Edge Cases

Empty Sections

When a section contains no items, the layout checks the isCanCalculateWhenRowEmpty property or delegates to collectionView:layout:isCanCalculateWhenRowEmptyWithSection:. If enabled, the layout calculates the background based solely on the header and footer frames, or the section's theoretical bounds if neither supplementary view exists.

Irregular Cell Sizes

For flow layouts with variable cell heights, enabling isCalculateTypeOpenIrregularitiesCell ensures the rounded background encompasses all cells regardless of their vertical position. The layout calls calculateIrregularitiesCellByMinTopFrameWithLayout: and calculateIrregularitiesCellByMaxBottomFrameWithLayout: in JJCollectionViewFlowLayoutUtils to determine the true bounds.

Touch Animations

If isDecorationViewTouchAnimationEnable is set, or if the delegate implements collectionView:layout:isDecorationViewShowAnimationAtIndex:, the decoration view receives a JJCollectionViewTouchAnimationConfigModel. The internal JJCollectionReusableView class handles tap gestures, applying scale transforms to both the background and the cells within that section.

Summary

  • Frame Calculation: The layout merges header, footer, and cell frames using CGRectUnion in JJCollectionViewRoundFlowLayout.m to determine total section bounds.
  • Delegate Control: Implement isCalculateHeaderViewIndex: and isCalculateFooterViewIndex: to selectively enable backgrounds per section, overriding the default isCalculateHeader and isCalculateFooter properties.
  • Inset Configuration: Use borderEdgeInsertsForSectionAtIndex: to specify spacing between the rounded background and section content, separate from standard sectionInset.
  • Edge Case Handling: Empty sections and irregular cell layouts are supported through dedicated delegate methods and calculation utilities in JJCollectionViewFlowLayoutUtils.
  • Visual Customization: The JJCollectionViewRoundConfigModel provides comprehensive styling options including corner radius, shadows, borders, and background colors rendered by JJCollectionReusableView.

Frequently Asked Questions

How do I disable header background calculation for specific sections?

Implement the collectionView:layout:isCalculateHeaderViewIndex: delegate method. Return NO for sections that should not display a background behind their headers. The layout defaults to the isCalculateHeader property value when the delegate method is not implemented.

What happens if a section has no items but I still want a rounded background?

Set the isCanCalculateWhenRowEmpty property to YES or implement collectionView:layout:isCanCalculateWhenRowEmptyWithSection: and return YES for the specific section. The layout will then calculate the background frame using only the header and footer attributes, or the section's theoretical bounds if neither exists.

How do custom border insets differ from standard section insets?

Border insets are specific to the rounded background decoration and are supplied via collectionView:layout:borderEdgeInsertsForSectionAtIndex:. They control the spacing between the edge of the rounded background and the content. Section insets (sectionInset) control the spacing between cells and the collection view boundaries. The layout applies both in JJCollectionViewRoundFlowLayout.m when calculating the final decoration view frame.

Can I animate the rounded background when the user taps on it?

Yes. Enable isDecorationViewTouchAnimationEnable on the layout, or implement collectionView:layout:isDecorationViewShowAnimationAtIndex: to enable animations for specific sections. The internal JJCollectionReusableView class handles touch events and applies scale animations using JJCollectionViewTouchAnimationConfigModel settings. If you don't provide a custom animation configuration, the layout uses default values from the model class.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →