188

I have a UICollectionView which shows photos. I have created the collectionview using UICollectionViewFlowLayout. It works good but I would like to have spacing on margins. Is it possible to do that using UICollectionViewFlowLayout or must I implement my own UICollectionViewLayout?

tjpaul
  • 332
  • 3
  • 16
Mert
  • 5,867
  • 3
  • 19
  • 32

15 Answers15

348

You can use the collectionView:layout:insetForSectionAtIndex: method for your UICollectionView or set the sectionInset property of the UICollectionViewFlowLayout object attached to your UICollectionView:

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

or

UICollectionViewFlowLayout *aFlowLayout = [[UICollectionViewFlowLayout alloc] init];
[aFlowLayout setSectionInset:UIEdgeInsetsMake(top, left, bottom, right)];

Updated for Swift 5

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
       return UIEdgeInsets(top: 25, left: 15, bottom: 0, right: 5)
    }
multitudes
  • 1,245
  • 1
  • 9
  • 22
michael23
  • 3,626
  • 1
  • 13
  • 10
  • 42
    I don't seem to be getting left and right insets to work when using a vertical flow layout... – John Apr 04 '13 at 00:25
  • 1
    Now overriding the sectionInset method of subclassed UICollectionViewFlowLayout also has no effect. Setting property described below works. – Dren Apr 29 '15 at 15:37
  • 7
    NB: remember to add `UICollectionViewDelegateFlowLayout` to your list of ViewController delegates to use the method `insetForSectionAtIndex` – David Douglas Aug 12 '16 at 22:54
101

Setting up insets in Interface Builder like shown in the screenshot below

Setting section insets for UICollectionView

Will result in something like this:

A collection view cell with section insets

krzymar
  • 1,205
  • 1
  • 9
  • 6
  • 2
    Great answer, just wanted to point out that you will also have to play with minimum spacing so that section inset works well. – Smit Yash Apr 11 '17 at 05:58
75

To add spacing on the entire UICollectionView:

UICollectionViewFlowLayout *flow = (UICollectionViewFlowLayout*) collection.collectionViewLayout;
flow.sectionInset = UIEdgeInsetsMake(topMargin, left, bottom, right);

To play with the spacing between elements of the same row (column if you're scrolling horizontally), and their sizes:

flow.itemSize = ...;
flow.minimumInteritemSpacing = ...;
Honey
  • 24,125
  • 14
  • 123
  • 212
marmor
  • 25,207
  • 10
  • 99
  • 145
  • 5
    This works, but the `collection.collectionViewLayout` needs a cast as in the example by @Pooja: `UICollectionViewFlowLayout *flow = (UICollectionViewFlowLayout*) self.collectionViewLayout;` – jwj Dec 05 '13 at 18:56
  • Casting in swift `if let flow = collectionViewLayout as? UICollectionViewFlowLayout { flow.itemSize = CGSize(width: 100, height: 100) } ` – emily May 31 '16 at 13:51
44

Swift 4

    let flow = collectionView.collectionViewLayout as! UICollectionViewFlowLayout 
    // If you create collectionView programmatically then just create this flow by UICollectionViewFlowLayout() and init a collectionView by this flow.

    let itemSpacing: CGFloat = 3
    let itemsInOneLine: CGFloat = 3
    flow.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)

    //collectionView.frame.width is the same as  UIScreen.main.bounds.size.width here.
    let width = UIScreen.main.bounds.size.width - itemSpacing * CGFloat(itemsInOneLine - 1) 
    flow.itemSize = CGSize(width: floor(width/itemsInOneLine), height: width/itemsInOneLine)
    flow.minimumInteritemSpacing = 3
    flow.minimumLineSpacing = itemSpacing

EDIT

If you want to change to scrollDirction horizontally:

flow.scrollDirection = .horizontal

NOTE

If you set items in one lines isn't correctly, check if your collection view has paddings. That is:

let width = UIScreen.main.bounds.size.width - itemSpacing * CGFloat(itemsInOneLine - 1)

should be the collectionView width.

enter image description here

William Hu
  • 12,918
  • 8
  • 85
  • 98
43

Just to correct some wrong information in this page:

1- minimumInteritemSpacing: The minimum spacing to use between items in the same row.

The default value: 10.0.

(For a vertically scrolling grid, this value represents the minimum spacing between items in the same row.)

2- minimumLineSpacing : The minimum spacing to use between lines of items in the grid.

Ref: http://developer.apple.com/library/ios/#documentation/uikit/reference/UICollectionViewFlowLayout_class/Reference/Reference.html

Felix
  • 751
  • 5
  • 10
  • 2
    This doesn't seem to affect spacing between sections, only between items within the same section? – Crashalot Jan 18 '16 at 17:32
  • @Crashalot - are you sure you don't mean minimumLineSpacing? i.e. every line of cells? There's also options for Section Insets if you really are using sections. – Chucky Jun 23 '16 at 16:45
  • This worked for me doing it via the storyboard interface. I was looking for a solution for horizontal only collection views. Thanks +10 – kemicofa ghost Nov 08 '16 at 15:23
10

Modern Swift, Automatic Layout Calculation

While this thread already contains a bunch of useful answers, I want to add a modern Swift version, based on William Hu's answer. It also improves two things:

  • The spacing between different lines will now always match the spacing between items in the same line.
  • By setting a minimum width, the code automatically calculates the number of items in a row and applies that style to the flow layout.

Here's the code:

// Create flow layout
let flow = UICollectionViewFlowLayout()

// Define layout constants
let itemSpacing: CGFloat = 1
let minimumCellWidth: CGFloat = 120
let collectionViewWidth = collectionView!.bounds.size.width

// Calculate other required constants
let itemsInOneLine = CGFloat(Int((collectionViewWidth - CGFloat(Int(collectionViewWidth / minimumCellWidth) - 1) * itemSpacing) / minimumCellWidth))
let width = collectionViewWidth - itemSpacing * (itemsInOneLine - 1)
let cellWidth = floor(width / itemsInOneLine)
let realItemSpacing = itemSpacing + (width / itemsInOneLine - cellWidth) * itemsInOneLine / max(1, itemsInOneLine - 1))

// Apply values
flow.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
flow.itemSize = CGSize(width: cellWidth, height: cellWidth)
flow.minimumInteritemSpacing = realItemSpacing
flow.minimumLineSpacing = realItemSpacing

// Apply flow layout
collectionView?.setCollectionViewLayout(flow, animated: false)
fredpi
  • 6,546
  • 3
  • 34
  • 57
8

use setMinimumLineSpacing: and setMinimumInteritemSpacing: on the UICollectionViewFlowLayout-Object.

Jonathan Cichon
  • 4,358
  • 14
  • 19
  • No it doesnt work, as expected. setMinimumLineSpacing is : The minimum spacing to use between items in the same row. And setMinimumInteritemSpacing is : The minimum spacing to use between lines of items in the grid. My question is about margins not space between elements. eg. Left and top spacing of first element. – Mert Dec 20 '12 at 11:26
  • assuming a vertical srollDirection you can use contentInset for top and bottom spacing of the first and last row. Also you could add your margins in your Cells. – Jonathan Cichon Dec 20 '12 at 11:53
8

Using collectionViewFlowLayout.sectionInset or collectionView:layout:insetForSectionAtIndex: are correct.

However, if your collectionView has multiple sections and you want to add margin to the whole collectionView, I recommend to use the scrollView contentInset :

UIEdgeInsets collectionViewInsets = UIEdgeInsetsMake(50.0, 0.0, 30.0, 0.0);
self.collectionView.contentInset = collectionViewInsets;
self.collectionView.scrollIndicatorInsets = UIEdgeInsetsMake(collectionViewInsets.top, 0, collectionViewInsets.bottom, 0);
vmeyer
  • 1,968
  • 19
  • 23
5

To put space between CollectionItems use this

write this two Line in viewdidload

UICollectionViewFlowLayout *collectionViewLayout = (UICollectionViewFlowLayout*)self.collectionView.collectionViewLayout;
collectionViewLayout.sectionInset = UIEdgeInsetsMake(<CGFloat top>, <CGFloat left>, <CGFloat bottom>, <CGFloat right>)
kmp
  • 9,895
  • 9
  • 66
  • 121
Pooja Jalan
  • 604
  • 7
  • 9
5

Set the insetForSectionAt property of the UICollectionViewFlowLayout object attached to your UICollectionView

Make sure to add this protocol

UICollectionViewDelegateFlowLayout

Swift

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

Objective - C

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section{
    return UIEdgeInsetsMake(top, left, bottom, right);
}
PinkeshGjr
  • 7,088
  • 5
  • 35
  • 52
2

In Objective-C

CGFloat spacing = 5;
UICollectionViewFlowLayout *flow = (UICollectionViewFlowLayout*)_habbitCollectionV.collectionViewLayout;
flow.sectionInset = UIEdgeInsetsMake(0, spacing, 0, spacing);
CGFloat itemsPerRow = 2;
CGRect screenRect = [[UIScreen mainScreen] bounds];
CGFloat oneMore = itemsPerRow + 1;
CGFloat width = screenRect.size.width - spacing * oneMore;
CGFloat height = width / itemsPerRow;

flow.itemSize = CGSizeMake(floor(height), height);
flow.minimumInteritemSpacing = spacing;
flow.minimumLineSpacing = spacing;

All you have to do is change the itemsPerRow value and it will update the number of items per row accordingly. Furthermore, you can change the spacing value if you want more or less general spacing.

enter image description here enter image description here

shim
  • 7,170
  • 10
  • 62
  • 95
ScottyBlades
  • 7,536
  • 2
  • 50
  • 60
1

For adding margins to specified cells, you can use this custom flow layout. https://github.com/voyages-sncf-technologies/VSCollectionViewCellInsetFlowLayout/

extension ViewController : VSCollectionViewDelegateCellInsetFlowLayout 
{
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForItemAt indexPath: IndexPath) -> UIEdgeInsets {
        if indexPath.item == 0 {
            return UIEdgeInsets(top: 0, left: 0, bottom: 10, right: 0)
        }
        return UIEdgeInsets.zero
    }
}
Myrddin
  • 81
  • 4
0

In swift 4 and autoLayout, you can use sectionInset like this:

let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .vertical
        layout.itemSize = CGSize(width: (view.frame.width-40)/2, height: (view.frame.width40)/2) // item size
        layout.sectionInset = UIEdgeInsetsMake(10, 10, 10, 10) // here you can add space to 4 side of item
        collectionView = UICollectionView(frame: self.view.bounds, collectionViewLayout: layout) // set layout to item
        collectionView?.register(ProductCategoryCell.self, forCellWithReuseIdentifier: cellIdentifier) // registerCell
        collectionView?.backgroundColor = .white // background color of UICollectionView
        view.addSubview(collectionView!) // add UICollectionView to view
Mohsen mokhtari
  • 2,115
  • 1
  • 21
  • 28
-1

To add 10px separation between each section just write this

flowLayout.sectionInset = UIEdgeInsetsMake(0.0, 0.0,10,0);
Gal Blank
  • 1,987
  • 2
  • 15
  • 18
-1
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {       
         return UIEdgeInsetsMake(7, 10, 5, 10);    
   }
Kampai
  • 21,517
  • 19
  • 87
  • 90
vishal
  • 11
  • 1