27

I'm making a simple UICollectionView with a paging mechanism enabled, and everything works fine. Though, when scroll to the last page the number of the cells are not fully visible in the screen, and the last page contains some cells of the previous page.

How do I expand the contentSize of the UICollectionView so that the last page doesn't contain any cells of the previous page?

An example here: the UICollectionView scrolls horizontally with 6 cells, this way:

  • Page 1:

    cell0 - cell1 - cell2 - cell3

  • Page 2:

    cell4 - cell5 is expected, but unexpectedly

    cell2 - cell3 - cell4 - cell5

How to change it?

SUMMARY:

I want to set

collectionView.contentSize = numberOfPage * collectionView.frame

NOT

collectionView.contentSize = numberOfCell * (cellFrame + spacing)

qtmfld
  • 2,916
  • 2
  • 21
  • 36
LE SANG
  • 10,955
  • 7
  • 59
  • 78
  • It should not happened actually. Can you plz post code snippet if any. – Swapnil Feb 13 '13 at 11:43
  • I try:[collectonView setContentSize] in viewDidLoad and viewDidAppear but It not work because contentSize will be rewrite in datasource. – LE SANG Feb 13 '13 at 11:48

6 Answers6

35

You need to subclass UICollectionViewLayout and override the collectionViewContentSize method. I subclassed UICollectionViewFlowLayout so I wouldn't have to re-write all the layout code.

I'm building a 4x4 grid, so my method looks like this:

- (CGSize)collectionViewContentSize
{    
    NSInteger itemCount = [self.collectionView numberOfItemsInSection:0];
    NSInteger pages = ceil(itemCount / 16.0);

    return CGSizeMake(320 * pages, self.collectionView.frame.size.height);
}

Side note, when you use a custom layout, you lose the ability to set the some of the display properties in the Interface Builder. You can set them programatically in the init method of your custom UICollectionViewLayout subclass. Here's mine for reference:

- (id)init
{
    self = [super init];
    if (self) {
        [self setup];
    }

    return self;
}

- (id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super init];
    if (self) {
        [self setup];
    }

    return self;
}

- (void)setup
{
    self.itemSize = CGSizeMake(65.0f, 65.0f);
    self.minimumLineSpacing = 15;
    self.sectionInset = UIEdgeInsetsMake(7.5f, 7.5f, 30.0f, 7.5f);
    [self setScrollDirection:UICollectionViewScrollDirectionHorizontal];
}
reallyseth
  • 586
  • 3
  • 10
  • I have been told that this sort of thing could cause a huge memory footprint because the whole collection view would be "shown" and the reusability of cells would not be possible. Is this true? – Mark T. Jul 23 '14 at 19:08
  • UICollectionView is already a subclass of UIScrollView and having contentSize is natural for it, I don't think you have to worry about cell reuseablity because all the cells are never visible at all. – Salman Khakwani Oct 14 '15 at 13:41
  • `collectionView.collectionViewLayout.collectionViewContentSize()` is what you need. If it's not valid, just `collectionView.layoutIfNeeded()` before you get contentSize. – DawnSong Mar 29 '16 at 13:47
  • 1
    How to set collectionViewContentSize in Swift 3.0 – AtulParmar Apr 12 '17 at 07:23
2

For horizontal paging

if(CollectionView.pagingEnabled)
{

    int numberOfPages = floor(collectionView.contentSize.width /
                      collectionView.frame.size.width) + 1;
                             CGFloat 
    float requierdWidth=self.collectionView.frame.size.width*numberOfPages;

    self.Layout.footerReferenceSize=CGSizeMake(requierdWidth-self.collectionView.contentSize.width,0);
}
tkanzakic
  • 5,499
  • 16
  • 34
  • 41
user2501116
  • 119
  • 1
  • 4
1

The answer works well, though for our code we had one section per page. So it meant the override for our layout class was just

-(CGSize) collectionViewContentSize {
    return CGSizeMake(CGRectGetWidth(self.collectionView.frame) * 
                 [self.collectionView numberOfSections],
                 CGRectGetHeight(self.collectionView.frame)) ;
}
christophercotton
  • 5,829
  • 2
  • 34
  • 49
0

You can adjust the content with the viewDidLayoutSubviews: method. This method gets called when the collection view and all the cells are placed in the view, so that you can adjust cell.

Cesare
  • 9,139
  • 16
  • 78
  • 130
Swapnil
  • 1,858
  • 2
  • 22
  • 49
0

I think you have to subclass UICollectionViewLayout and create your custom layout to manage these kind of problems.

Andrea Mario Lufino
  • 7,921
  • 12
  • 47
  • 78
0

I have an answer that doesn't require any subclassing.

In -viewDidLoad, calculate how many items per page you will have and how many pages you will have. Store these values in properties.

    self.numberOfItemsPerPage = NumberOfRows * NumberOfColumns;
    self.numberOfPages = ceilf((CGFloat)self.items.count / (CGFloat)self.numberOfItemsPerPage);

Then in -collectionView:numberOfItemsInSection: just lie to it:

    return self.numberOfItemsPerPage * self.numberOfPages;

You will of course have more cells than content, right? So in -collectionView:cellForItemAtIndexPath: just return nil for the extra cells:

    if (indexPath.item > [self.items count] - 1) {
    //We have no more items, so return nil. This is to trick it to display actual full pages.
    return nil;
    }

There you go: full, scrollable final page. In my opinion, the horizontal scroll mode should just default to this.

Paul Bruneau
  • 1,026
  • 1
  • 9
  • 15
  • Pretty sure a cell can't be nil :( I tried this and got this error: `*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'the collection view's data source did not return a valid cell from -collectionView:cellForItemAtIndexPath: for index path ` – taber Jul 16 '14 at 05:22