27

I have subclassed UICollectionViewFlowLayout to get horizontal UICollectionView with paging like behavior. It works perfectly fine as long as UICollectionViewCell is not first of last cell. Images attached below.

enter image description here enter image description here

Do I need to override something in my UICollectionViewFlowLayout besides following ?

- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity
{
CGFloat offSetAdjustment = MAXFLOAT;
CGFloat horizontalCenter = (CGFloat) (proposedContentOffset.x + (self.collectionView.bounds.size.width / 2.0));

CGRect targetRect = CGRectMake(proposedContentOffset.x,
                               0.0,
                               self.collectionView.bounds.size.width,
                               self.collectionView.bounds.size.height);

NSArray *array = [self layoutAttributesForElementsInRect:targetRect];
for (UICollectionViewLayoutAttributes *layoutAttributes in array)
{
    if(layoutAttributes.representedElementCategory == UICollectionElementCategoryCell)
    {
        CGFloat itemHorizontalCenter = layoutAttributes.center.x;
        if (ABS(itemHorizontalCenter - horizontalCenter) < ABS(offSetAdjustment))
        {
            offSetAdjustment = itemHorizontalCenter - horizontalCenter;
        }
    }
}

CGFloat nextOffset = proposedContentOffset.x + offSetAdjustment;

do {
    proposedContentOffset.x = nextOffset;
    CGFloat deltaX = proposedContentOffset.x - self.collectionView.contentOffset.x;
    CGFloat velX = velocity.x;

    if(deltaX == 0.0 || velX == 0 || (velX > 0.0 && deltaX > 0.0) || (velX < 0.0 && deltaX < 0.0))
    {
        break;
    }

    if(velocity.x > 0.0)
    {
        nextOffset += [self snapStep];
    }
    else if(velocity.x < 0.0)
    {
        nextOffset -= [self snapStep];
    }
} while ([self isValidOffset:nextOffset]);

proposedContentOffset.y = 0.0;

return proposedContentOffset;
}
    - (BOOL)isValidOffset:(CGFloat)offset
{
    return (offset >= [self minContentOffset] && offset <= [self maxContentOffset]);
}

- (CGFloat)minContentOffset
{
  return -self.collectionView.contentInset.left;
}

- (CGFloat)maxContentOffset
{
    return [self minContentOffset] + self.collectionView.contentSize.width -      self.itemSize.width;
}

- (CGFloat)snapStep
{
return self.itemSize.width + self.minimumLineSpacing;
}

Any pointers/ comments will be useful.

slonkar
  • 4,055
  • 8
  • 39
  • 63
  • you can use `UIEdgeInsets sectionInset` property of `UICollectionViewFlowLayout`. Refer this [post](http://stackoverflow.com/questions/16934831/placing-a-margin-around-each-edge-of-the-uicollectionview). Also refer Apple Documentation on [Using section inset](https://developer.apple.com/library/ios/documentation/WindowsViews/Conceptual/CollectionViewPGforIOS/UsingtheFlowLayout/UsingtheFlowLayout.html) – Dipen Panchasara Aug 24 '16 at 07:44

6 Answers6

50

You can set space at left and right equal to your padding when setting frame of your collection view.

or

You can put condition in cellForItemAtIndexPath that if it is first cell or last cell then manage padding accordingly. that's it.

Or

you can set contentInset property of your collectionView.

for example,

UICollectionView *cv; // your collectionView

cv.contentInset = UIEdgeInsetsMake(0, 5, 0, 5);

Alternatively, you can set the UICollectionView contentInset in storyboard to get it working.

Pranav Kasetti
  • 8,770
  • 2
  • 50
  • 71
Ketan Parmar
  • 27,092
  • 9
  • 50
  • 75
  • argh I have been toying with section inset for last 30 min never thought of setting inset of UICollectionView itself. – slonkar Aug 24 '16 at 08:04
  • yeah, setting contentInset is good solution for your case. :) – Ketan Parmar Aug 24 '16 at 08:09
  • The contentInset makes sense, but putting it in cellForItemAtIndexPath doesn't work well if it is the last item and you append another, as, if you are scrolled to the end and a new item gets added, cellForItemAtIndexPath typically gets called for the NEW final item, but not called again for the prior final item. Or at least not guaranteed to be called. – drewster Mar 26 '18 at 14:49
  • 1
    no you should not set contentinsett in `cellForItemAtIndexPath` and I haven't said like that in my answer also! – Ketan Parmar Mar 27 '18 at 05:09
10

You can achieve that by changing the insets from the Interface Builder. They can be found as Section Insets in the Size Inspector:

Section Insets for Collection View in InterfaceBuilder

Jonathan Cabrera
  • 1,656
  • 1
  • 19
  • 20
8

Accepted solution works but if you have pagingEnabled the collection view paging gets broken.

For me the solution was to use:


func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
    return UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 16)
}

Tung Fam
  • 7,899
  • 4
  • 56
  • 63
4

For Swift 5:

In your viewDidLoad, set contentInset property of the collectionView like so:

self.collectionView.contentInset = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5);
Rbar
  • 3,740
  • 9
  • 39
  • 69
3

simple you can use collectionview methods to set UIEdgeInsets like bellow method.

-(UIEdgeInsets)collectionView:(UICollectionView*)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
{
  return UIEdgeInsetsMake(0,10,0,10); // top, left, bottom, right
}

here you can pass the value for left side space and right side space for first and last cell, you can also provide minimum space between two cell through bellow method

- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section {
   return 5.0;
}
Pankaj K.
  • 535
  • 8
  • 18
  • I am already setting inset when I create instance of UICollectionViewFlowLayout. So it does not help and minimumInteritemSpacing is not useful here because I am doing horizontal scroll. You have to use minimumLineSpacing here which I am already doing. – slonkar Aug 24 '16 at 08:00
0

You can use this piece of code for padding

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {

    return UIEdgeInsets(top: 60, left: 0, bottom: 40, right: 0)
}
CodeChanger
  • 7,953
  • 5
  • 49
  • 80