4

I have a UICollectionView with a grey background color, however I want one of my sections to have a white background. I have discovered this was not as simple as I had hoped (i.e. the section having a attribute I could just set).

From looking at How to change background color of a whole section in UICollectionView? it was suggested to use a decoration view and this question UICollectionView Decoration View has helped me change the section color by subclassing UICollectionReusableView and UICollectionViewFlowLayout.

My issue is that I cannot get the section height (which is dynamic) to set the UIView height, and also the beginning of my following section is blank until there is a reloadData call on it.

Here is my code so far: WhiteBackgroundCollectionReusableView Class:

#import <UIKit/UIKit.h>

@interface WhiteBackgroundCollectionReusableView : UICollectionReusableView

@end

@implementation WhiteBackgroundCollectionReusableView

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
        [self setBackgroundColor:[UIColor redColor]];
    }
    return self;
}

@end

WhiteBackgroundCollectionViewFlowLayout Class:

#import <UIKit/UIKit.h>

#import "WhiteBackgroundCollectionReusableView.h"

@interface WhiteBackgroundCollectionViewFlowLayout : UICollectionViewFlowLayout

@end

@implementation WhiteBackgroundCollectionViewFlowLayout

- (id)init
{
    self = [super init];
    if (self) {
        // Initialization code

        [self registerClass:[WhiteBackgroundCollectionReusableView class] forDecorationViewOfKind:@"WhiteSection"];
    }
    return self;
}

- (UICollectionViewLayoutAttributes*)layoutAttributesForDecorationViewOfKind:(NSString*)decorationViewKind atIndexPath:(NSIndexPath*)indexPath
{
    UICollectionViewLayoutAttributes *layoutAttributes = [UICollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:decorationViewKind withIndexPath:indexPath];

    if(indexPath.section == 0) {
        layoutAttributes.frame = CGRectMake(0.0, 0.0, self.collectionViewContentSize.width, 100);

        layoutAttributes.zIndex = -1;
    }

    return layoutAttributes;
}

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    NSMutableArray *allAttributes = [[NSMutableArray alloc] initWithCapacity:4];

    [allAttributes addObject:[self layoutAttributesForDecorationViewOfKind:@"WhiteSection" atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]];

    for(NSInteger i = 0; i < [self.collectionView numberOfItemsInSection:0]; i++)
    {
        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
        UICollectionViewLayoutAttributes *layoutAttributes = [self layoutAttributesForItemAtIndexPath:indexPath];
        [allAttributes addObject:layoutAttributes];
    }

    return allAttributes;
}

@end

I then assign my flow layout to my UICollectionView in my UICollectionViewController in viewDidLoad:

[_collectionView setCollectionViewLayout:[[WhiteBackgroundCollectionViewFlowLayout alloc] init]];

Any idea where I can go from here??

UPDATE: I've fixed the issue where the next section was not showing. To do this I needed to change the zIndex to 1, so my layoutAttributesForElementsInRect function now looks like this:

- (NSArray*)layoutAttributesForElementsInRect:(CGRect)rect
{
    NSArray *array = [super layoutAttributesForElementsInRect:rect];

    for(UICollectionViewLayoutAttributes *attributes in array) {
        attributes.zIndex = 1;
    }

    NSMutableArray *newArray = [array mutableCopy];

    [newArray addObject:[self layoutAttributesForDecorationViewOfKind:@"WhiteSection" atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]];

    for(NSInteger i = 0; i < [self.collectionView numberOfItemsInSection:0]; i++) {
        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
        UICollectionViewLayoutAttributes *layoutAttributes = [self layoutAttributesForItemAtIndexPath:indexPath];
        [newArray addObject:layoutAttributes];
    }

    array = [NSArray arrayWithArray:newArray];

    return array;
}

So for now all I need is a way of getting the height of a section! PLEASE HELP! :)

Community
  • 1
  • 1
Damien
  • 1,647
  • 2
  • 16
  • 17

2 Answers2

1

My solution is available on https://github.com/s-atif-jamil/CollectionSectionView

In SectionBackgroundLayout.h

@interface SectionBackgroundLayout : UICollectionViewFlowLayout

@property (assign, nonatomic) BOOL alternateBackgrounds;
@property (strong, nonatomic) NSArray *decorationViewOfKinds;

@end

In SectionBackgroundLayout.m

#import "SectionBackgroundLayout.h"

@interface SectionBackgroundLayout ()

@property (strong, nonatomic) NSMutableArray *itemAttributes;

@end

@implementation SectionBackgroundLayout


- (void)prepareLayout
{
    [super prepareLayout];
    self.itemAttributes = [NSMutableArray new];

    NSInteger numberOfSection = self.collectionView.numberOfSections;
    for (int section=0; section<numberOfSection; section++)
    {
        if (!self.alternateBackgrounds && section == self.decorationViewOfKinds.count)
            break;

        NSString *decorationViewOfKind = self.decorationViewOfKinds[section % self.decorationViewOfKinds.count];
        if ([decorationViewOfKind isKindOfClass:[NSNull class]])
            continue;

        NSInteger lastIndex = [self.collectionView numberOfItemsInSection:section] - 1;
        if (lastIndex < 0)
            continue;

        UICollectionViewLayoutAttributes *firstItem = [self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:section]];
        UICollectionViewLayoutAttributes *lastItem = [self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForRow:lastIndex inSection:section]];

        CGRect frame = CGRectUnion(firstItem.frame, lastItem.frame);
        frame.origin.x -= self.sectionInset.left;
        frame.origin.y -= self.sectionInset.top;

        if (self.scrollDirection == UICollectionViewScrollDirectionHorizontal)
        {
            frame.size.width += self.sectionInset.left + self.sectionInset.right;
            frame.size.height = self.collectionView.frame.size.height;
        }
        else
        {
            frame.size.width = self.collectionView.frame.size.width;
            frame.size.height += self.sectionInset.top + self.sectionInset.bottom;
        }


        UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:decorationViewOfKind withIndexPath:[NSIndexPath indexPathForRow:0 inSection:section]];
        attributes.zIndex = -1;
        attributes.frame = frame;
        [self.itemAttributes addObject:attributes];
        [self registerNib:[UINib nibWithNibName:decorationViewOfKind bundle:[NSBundle mainBundle]] forDecorationViewOfKind:decorationViewOfKind];
    }
}

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    NSMutableArray *attributes = [NSMutableArray arrayWithArray:[super layoutAttributesForElementsInRect:rect]];
    for (UICollectionViewLayoutAttributes *attribute in self.itemAttributes)
    {
        if (!CGRectIntersectsRect(rect, attribute.frame))
            continue;

        [attributes addObject:attribute];
    }

    return attributes;
}

@end

In view controller

- (void)viewDidLoad
{
    [super viewDidLoad];
    SectionBackgroundLayout *layout = (id)self.collectionView.collectionViewLayout;
    layout.decorationViewOfKinds = @[@"SectionBackgroundView1", @"SectionBackgroundView2", [NSNull null]];
    ...
}
Atif
  • 1,220
  • 11
  • 16
0

I am using this method to calculate section size

-(CGRect) rectForSectionAtIndextPath:(NSIndexPath *) indexPath
{
  NSInteger count = [self.collectionView.dataSource collectionView:self.collectionView numberOfItemsInSection:indexPath.section];

UICollectionViewLayoutAttributes *first = [self.collectionView layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:indexPath.section]];
CGFloat startY = first.frame.origin.y;

UICollectionViewLayoutAttributes *last = [self.collectionView layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:(count-1) inSection:indexPath.section]];
CGFloat endY = last.frame.origin.y + last.frame.size.height;

return CGRectMake(0, startY, self.collectionViewContentSize.width, endY-startY);

 }
MP23
  • 1,763
  • 20
  • 25