6
votes

I'm attempting to define a custom flow layout for a UICollectionView by subclassing UICollectionViewFlowLayout. The layout I want looks like this:

layout

As you can see, I have split the collection view into 2 equal rows along the horizontal middle divide. The top row is split into thirds to give 3 columns, and the bottom rows split in half to give 2 columns.

Everything seems to work and look fine until you scroll to the right (the collection view scrolling is to UICollectionViewScrollDirectionHorizontal), and see there a large space after the 5 cells have finished being rendered:

layout

I have no idea why this is happening, however it does appear that the empty space looks about the same width as a cell from the top row; a third of the width of the collection view.

Here is how I setup the UICollectionView and UICollectionViewFlowLayout subclass

- (void)loadView {
    [super loadView];
    [self setupCollectionView];
}

- (void)setupCollectionView {
    CustomFlowLayout *flowLayout = [[CustomFlowLayout alloc] init];
    flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
    flowLayout.minimumInteritemSpacing = 0.0;
    flowLayout.minimumLineSpacing = 0.0f;

    CGRect frame = CGRectMake(0, 0, 748, 1024);
    UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:frame collectionViewLayout:flowLayout];

    [collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:CollectionViewCellID];

    collectionView.delegate = self;
    collectionView.dataSource = self;

    collectionView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;

    collectionView.backgroundColor = [UIColor whiteColor];

    collectionView.pagingEnabled = YES;


    self.collectionView = collectionView;

    [self.view addSubview:self.collectionView];
}

#pragma mark - UICollectionViewDataSource
- (NSInteger )numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
    return 1;
}

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {

    return 5;
}

The size of the items in the collection view is defined by the layout configuration for a given section, so I implement collectionView:layout:sizeForItemAtIndexPath: like so:

- (CGSize)collectionView:(UICollectionView *)collectionView
                  layout:(UICollectionViewLayout*)collectionViewLayout
  sizeForItemAtIndexPath:(NSIndexPath *)indexPath {

    return [collectionViewLayout layoutAttributesForItemAtIndexPath:indexPath].size;

}

My custom subclass of UICollectionViewLayout is implemented as follows:

@implementation CustomFlowLayout

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
    NSArray* attributesToReturn = [super layoutAttributesForElementsInRect:rect];
    for (UICollectionViewLayoutAttributes* attributes in attributesToReturn) {
        if (nil == attributes.representedElementKind) {
            NSIndexPath* indexPath = attributes.indexPath;
            attributes.frame = [self layoutAttributesForItemAtIndexPath:indexPath].frame;
        }
    }
    return attributesToReturn;
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewLayoutAttributes* currentItemAttributes = [super layoutAttributesForItemAtIndexPath:indexPath];

    if (!currentItemAttributes) {
        currentItemAttributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    }

    float height = self.collectionView.frame.size.height;
    float width = self.collectionView.frame.size.width;

    CGRect frame = CGRectZero;    
    switch (indexPath.row) {
        case 0:
            frame.size = CGSizeMake(width/3, height/2);
            frame.origin = CGPointMake(0, 0);
            break;
        case 1:
            frame.size =  CGSizeMake(width/3, height/2);
            frame.origin = CGPointMake(width/3, 0);
            break;
        case 2:
            frame.size =  CGSizeMake(width/3, height/2);
            frame.origin = CGPointMake(2*width/3, 0);
            break;
        case 3:
            frame.size =  CGSizeMake(width/2, height/2);
            frame.origin = CGPointMake(0, height/2);
            break;
        case 4:
            frame.size =  CGSizeMake(width/2, height/2);
            frame.origin = CGPointMake(width/2, height/2);
            break;
        default:
            break;
    }

    currentItemAttributes.frame = frame;
    currentItemAttributes.size = frame.size;
    return currentItemAttributes;
}

Anyone got any ideas why I have the large empty space after all the cells have been rendered? Or does anyone have a better approach to rendering the layout I am after. All the code is available as a sample XCode project here. Answers, tips, thoughts, pull requests are greatly appreciated.

1

1 Answers

4
votes

The problem seemed to be that when there were two rows of cells that had different widths the contentSize of the collectionview was being calculated in such a way that left an area of white space to the right. A simple was necessary to fix the problem:

- (CGSize)collectionViewContentSize {
    return CGSizeMake(1024, 748);
}

Updated the Github repo with the changes here. Hope this helps someone in the future.