145

I'm trying to add UICollectionView to ViewController, and I need to have 3 cells 'per row' without blank space between cells (it should look like a grid). Cell width should be one third of screen size, so I thought that the layout.item width should be the same. But then I get this:

layout.itemSize width = screenSize.width/3

If I reduce that size (by 7 or 8 pixels e.g.), it's better, but the third cell in row is not completely visible, and I still have that blank space (top & bottom, and left & right) .

enter image description here

class ViewController: UIViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
    var collectionView: UICollectionView?
    var screenSize: CGRect!
    var screenWidth: CGFloat!
    var screenHeight: CGFloat!

    override func viewDidLoad() {
        super.viewDidLoad()

        screenSize = UIScreen.mainScreen().bounds
        screenWidth = screenSize.width
        screenHeight = screenSize.height

        // Do any additional setup after loading the view, typically from a nib
        let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
        layout.sectionInset = UIEdgeInsets(top: 20, left: 0, bottom: 10, right: 0)
        layout.itemSize = CGSize(width: screenWidth / 3, height: screenWidth / 3)
        collectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
        collectionView!.dataSource = self
        collectionView!.delegate = self
        collectionView!.registerClass(CollectionViewCell.self, forCellWithReuseIdentifier: "CollectionViewCell")
        collectionView!.backgroundColor = UIColor.greenColor()
        self.view.addSubview(collectionView!)
    }

    func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
        return 1
    }

    func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 20
    }

    func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier("CollectionViewCell", forIndexPath: indexPath) as CollectionViewCell
        cell.backgroundColor = UIColor.whiteColor()
        cell.layer.borderColor = UIColor.blackColor().CGColor
        cell.layer.borderWidth = 0.5
        cell.frame.size.width = screenWidth / 3
        cell.frame.size.height = screenWidth / 3

        cell.textLabel?.text = "\(indexPath.section):\(indexPath.row)"
        return cell
    }
}
Bright Future
  • 4,961
  • 2
  • 47
  • 67
Marko
  • 1,791
  • 4
  • 13
  • 17

10 Answers10

324

Add these 2 lines

layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 0

So you have:

   // Do any additional setup after loading the view, typically from a nib.
        let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
        layout.sectionInset = UIEdgeInsets(top: 20, left: 0, bottom: 10, right: 0)
        layout.itemSize = CGSize(width: screenWidth/3, height: screenWidth/3)
        layout.minimumInteritemSpacing = 0
        layout.minimumLineSpacing = 0
        collectionView!.collectionViewLayout = layout

That will remove all the spaces and give you a grid layout:

enter image description here

If you want the first column to have a width equal to the screen width then add the following function:

func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
    if indexPath.row == 0
    {
        return CGSize(width: screenWidth, height: screenWidth/3)
    }
    return CGSize(width: screenWidth/3, height: screenWidth/3);

}

Grid layout will now look like (I've also added a blue background to first cell): enter image description here

Mehul
  • 2,500
  • 18
  • 30
Peter Todd
  • 7,971
  • 3
  • 29
  • 38
  • Thank you so much, I was expecting something much more complicated, and I spent to much time on it. Thanks one more time. – Marko Feb 05 '15 at 07:36
  • And just one thing more, is there any possibility to change _layout.itemSize_ for the first cell only? (I know that i need to change cell.frame.size when the _indexPath.row_ is 0, but don't know how to change layout size only for one item) First element width should be equal _screenWidth_. – Marko Feb 05 '15 at 08:15
  • I've edited my answer to show how to change the cell width. Also, you don't need the size setting code in cellForItemAtIndexPath. You already set the size using layout.itemSize in viewDidLoad. The sizeForItemAtIndexPath function that I've added enables you to override the size. Hope that helps. – Peter Todd Feb 05 '15 at 11:42
  • 1
    Hi, I have the same problem but i want to know the similar function title in Objective C . – Nada Gamal Mar 16 '15 at 21:13
  • 1
    Ok , It works fine now , Thanks a lot :) Objective C function : -(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { return CGSizeMake(screenWidth/3, screenWidth/3); } – Nada Gamal Mar 16 '15 at 21:22
  • 4
    thanks for the great answer @greentor. sent a bounty. Don't forget the **collectionView!.collectionViewLayout = layout**, readers – Fattie Jun 29 '16 at 21:23
  • Really helpful :) – iDeveloper Nov 05 '16 at 06:44
  • I added my cell from storyboard not like this, how I can change the space? – amjad Dec 05 '16 at 17:25
  • @greentor Could you answer me please ? – amjad Dec 05 '16 at 17:26
  • @amjad hava a look at https://github.com/petetodd/BGSEmbeddedCVSwift for an example. In the storyboard size inspector you can change some of the collection view layout settings but you will probably need to do some calculations and set some layout values in the code. – Peter Todd Dec 05 '16 at 18:23
  • @greentor thank you, but what if I want a little space between the columns "like 4 or 5" what I should change in the code ? because when I write `layout.minimumInteritemSpacing = 4` doesn't work well – amjad Dec 05 '16 at 18:40
  • @amjad you will need to adjust the size of the collection view cell (the line return CGSize(width: screenWidth/3, height: screenWidth/3)) – Peter Todd Dec 05 '16 at 18:54
50

For Swift 3 and XCode 8, this worked. Follow below steps to achieve this:-

{
    let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
    let width = UIScreen.main.bounds.width
    layout.sectionInset = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5)
    layout.itemSize = CGSize(width: width / 2, height: width / 2)
    layout.minimumInteritemSpacing = 0
    layout.minimumLineSpacing = 0
    collectionView!.collectionViewLayout = layout
}

Place this code into viewDidLoad() function.

EES
  • 1,484
  • 3
  • 19
  • 36
Nirmit Dagly
  • 1,103
  • 10
  • 21
39

In Certain situations, Setting the UICollectionViewFlowLayout in viewDidLoador ViewWillAppear may not effect on the collectionView.

Setting the UICollectionViewFlowLayout in viewDidAppear may cause see the changes of the cells sizes in runtime.

Another Solution, in Swift 3 :

extension YourViewController : UICollectionViewDelegateFlowLayout{

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

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let collectionViewWidth = collectionView.bounds.width
        return CGSize(width: collectionViewWidth/3, height: collectionViewWidth/3)
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        return 0
    }
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return 20
    }
}
Maor
  • 2,652
  • 3
  • 25
  • 34
  • Great Answer! This definitely is the Apple way to do it. Now, you don't happen to know how to refresh when device changes orientation, do you? – jlstr Oct 12 '17 at 22:20
19

If you are looking for Swift 3, Follow the steps to achieve this:

func viewDidLoad() {

    //Define Layout here
    let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()

    //Get device width
    let width = UIScreen.main.bounds.width

    //set section inset as per your requirement.
    layout.sectionInset = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5)

    //set cell item size here 
    layout.itemSize = CGSize(width: width / 2, height: width / 2)

    //set Minimum spacing between 2 items
    layout.minimumInteritemSpacing = 0

    //set minimum vertical line spacing here between two lines in collectionview
    layout.minimumLineSpacing = 0

    //apply defined layout to collectionview
    collectionView!.collectionViewLayout = layout

}

This is verified on Xcode 8.0 with Swift 3.

9
let layout = myCollectionView.collectionViewLayout as? UICollectionViewFlowLayout
layout?.minimumLineSpacing = 8
brimstone
  • 3,165
  • 3
  • 27
  • 47
jamal zare
  • 563
  • 6
  • 8
4

Swift 4

let collectionViewLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout
collectionViewLayout?.sectionInset = UIEdgeInsetsMake(0, 20, 0, 40) 
collectionViewLayout?.invalidateLayout()
Ahmadreza
  • 5,262
  • 2
  • 36
  • 55
1

For Swift 3 and XCode 8, this worked. Follow below steps to achieve this:-

viewDidLoad()
    {
        let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
        var width = UIScreen.main.bounds.width
        layout.sectionInset = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5)
    width = width - 10
    layout.itemSize = CGSize(width: width / 2, height: width / 2)
        layout.minimumInteritemSpacing = 0
        layout.minimumLineSpacing = 0
        collectionView!.collectionViewLayout = layout
    }
Moin Shirazi
  • 4,116
  • 2
  • 23
  • 36
0

For Swift 3+ and Xcode 9+ Try using this

extension ViewController: UICollectionViewDelegateFlowLayout {

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

    let collectionWidth = collectionView.bounds.width
    return CGSize(width: collectionWidth/3, height: collectionWidth/3)

}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
    return 0
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
    return 0

}
Aditya
  • 1,369
  • 9
  • 19
0

Swift 5 : For evenly distributed spaces between cells with dynamic cell width to make the best of container space you may use the code snippet below by providing a minimumCellWidth value.

private func collectionViewLayout() -> UICollectionViewLayout {
    
    let layout = UICollectionViewFlowLayout()
    layout.sectionHeadersPinToVisibleBounds = true
    // Important: if direction is horizontal use minimumItemSpacing instead. 
    layout.scrollDirection = .vertical
    
    let itemHeight: CGFloat = 240
    let minCellWidth :CGFloat = 130.0
    let minItemSpacing: CGFloat = 10
    let containerWidth: CGFloat = self.view.bounds.width
    let maxCellCountPerRow: CGFloat =  floor((containerWidth - minItemSpacing) / (minCellWidth+minItemSpacing ))
    
    let itemWidth: CGFloat = floor( ((containerWidth - (2 * minItemSpacing) - (maxCellCountPerRow-1) * minItemSpacing) / maxCellCountPerRow  ) )
    // Calculate the remaining space after substracting calculating cellWidth (Divide by 2 because of left and right insets)
    let inset = max(minItemSpacing, floor( (containerWidth - (maxCellCountPerRow*itemWidth) - (maxCellCountPerRow-1)*minItemSpacing) / 2 ) )

    
    layout.itemSize = CGSize(width: itemWidth, height: itemHeight)
    layout.minimumInteritemSpacing = min(minItemSpacing,inset)
    layout.minimumLineSpacing = minItemSpacing
    layout.sectionInset = UIEdgeInsets(top: minItemSpacing, left: inset, bottom: minItemSpacing, right: inset)

    
    return layout
}
0

Swift 4, Swift 5, Easiest Way!

No need create UICollectionViewFlowLayout() instance, just implement UICollectionViewDelegateFlowLayout on your Class.

extension MyCollectionView: UICollectionViewDelegateFlowLayout {

    // Distance Between Item Cells
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return 20
    }
    
    // Cell Margin
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
        return UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16)
    }

}
Pengguna
  • 3,249
  • 1
  • 19
  • 26