37

I've been trying to figure-out how can i make the cell fill the width, as you can see in the picture the width between the cells is too big. i am using custom cell with only one imageView.

enter image description here

I tried to customize it from the storyboard but i guess there is no option for that or it should be done programmatically. enter image description here

my UICollectionViewController :

 @IBOutlet var collectionView2: UICollectionView!

    let recipeImages = ["angry_birds_cake", "creme_brelee", "egg_benedict", "full_breakfast", "green_tea", "ham_and_cheese_panini", "ham_and_egg_sandwich", "hamburger", "instant_noodle_with_egg.jpg", "japanese_noodle_with_pork", "mushroom_risotto", "noodle_with_bbq_pork", "starbucks_coffee", "thai_shrimp_cake", "vegetable_curry", "white_chocolate_donut"]

    override func viewDidLoad() {
        super.viewDidLoad()

        // Uncomment the following line to preserve selection between presentations
        // self.clearsSelectionOnViewWillAppear = false

        // Do any additional setup after loading the view.

    }


    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
        //#warning Incomplete method implementation -- Return the number of sections
        return 1
    }


    override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        //#warning Incomplete method implementation -- Return the number of items in the section
         return recipeImages.count
    }

    override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! RecipeCollectionViewCell

        // Configure the cell
        cell.recipeImageView.image = UIImage(named: recipeImages[indexPath.row])

        return cell
    }
AaoIi
  • 8,288
  • 6
  • 45
  • 87
  • just check this answer http://stackoverflow.com/questions/29649687/reduce-space-between-cells-in-a-collectionview/29649994#29649994 – Bhavin Bhadani May 01 '15 at 11:58
  • What is the contentResizingMode of your image view? ...and are you using auto layout? – Rog May 01 '15 at 12:11
  • Well its in different sizes for example : 560 × 350 , no I'm not using any kind of auto layout. – AaoIi May 01 '15 at 12:17

11 Answers11

94

You need to do this programatically.

Implement UICollectionViewDelegateFlowLayout in your view controller and provide the size in collectionView:layout:sizeForItemAtIndexPath:

func collectionView(_ collectionView: UICollectionView,
                        layout collectionViewLayout: UICollectionViewLayout,
                        sizeForItemAt indexPath: IndexPath) -> CGSize {
        let kWhateverHeightYouWant = 100
        return CGSizeMake(collectionView.bounds.size.width, CGFloat(kWhateverHeightYouWant))
}

You will also want to call collectionView.collectionViewLayout.invalidateLayout() inside your view controller's viewWillLayoutSubviews() so that when the main view's dimensions change (on rotation, for example), the collection view is re-laid out.

Swift 4 Update

func collectionView(_ collectionView: UICollectionView,
                            layout collectionViewLayout: UICollectionViewLayout,
                            sizeForItemAt indexPath: IndexPath) -> CGSize {
            let kWhateverHeightYouWant = 100
            return CGSize(width: collectionView.bounds.size.width, height: CGFloat(kWhateverHeightYouWant))
}
Matthew Quiros
  • 13,385
  • 12
  • 87
  • 132
  • i did exactly as you said, but when i run the app, it hangs on the launch screen and the Xcode shows this message "Message from debugger: Terminated due to Memory Error" !! – AaoIi May 01 '15 at 12:54
  • It seems that the memory error is coming from somewhere else. Do you have a stack trace that I can look at? Or take a picture of the Debug Navigator or breakpoint if there are no logs. – Matthew Quiros May 01 '15 at 13:12
  • its a window without any stack trace , it says .. The app "CollectionView" on "** iPhone" quit unexpectedly. Message from debugger: Terminated due to Memory Error . and Ok button – AaoIi May 01 '15 at 13:18
  • Google the error message. I'm guessing you have a retain cycle somewhere. – Matthew Quiros May 01 '15 at 13:38
  • ok, i figured to let it compile but the result as shown : http://i921.photobucket.com/albums/ad53/Johann_1990/IMG_0222.png , should be two images in the same row stick to each other not only one. – AaoIi May 01 '15 at 14:00
  • @Aoli You just need to provide `collectionView.bounds.size.width / 2` in `CGSizeMake()`, then. Also, change your image view's `contentMode` property to either `ScaleAspectFit` or `ScaleAspectFill`. – Matthew Quiros May 01 '15 at 14:12
  • If you just want to affect the width how do you know (in that function) what the height was to have been? – dumbledad Oct 31 '16 at 10:53
  • 2
    Your tip on invalidating the layout was what I've been looking for, for hours. Thank you. – WBuck Mar 16 '18 at 00:49
11

Use Following code for set the width of UICollectionViewCell.

func collectionView(_ collectionView: UICollectionView,
                    layout collectionViewLayout: UICollectionViewLayout,
                    sizeForItemAt indexPath: IndexPath) -> CGSize {        
    return CGSize(width: screenWidth/3, height: screenWidth/3);
}
DarkDust
  • 90,870
  • 19
  • 190
  • 224
Nimit Parekh
  • 16,776
  • 8
  • 50
  • 72
11

Inside your view controller override viewDidLayoutSubviews method

@IBOutlet weak var collectionView: UICollectionView!

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    if let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
        let itemWidth = view.bounds.width / 3.0
        let itemHeight = layout.itemSize.height
        layout.itemSize = CGSize(width: itemWidth, height: itemHeight)
        layout.invalidateLayout()
    }
}

(collectionView property is your collectionView)

mustafa
  • 15,254
  • 10
  • 48
  • 57
  • its giving me "Terminated due to Memory Error" on runtime ? – AaoIi May 01 '15 at 12:11
  • Possibly because it's causing an infinite loop. When the view is done laying out the subviews (what `viewDidLayoutSubviews` is for), you invalidate the layout and lay it out again. – Matthew Quiros Oct 02 '16 at 19:53
8

Also in Swift 3,

Make sure your view controller complies with the following:

UICollectionViewDelegate,
UICollectionViewDataSource,
UICollectionViewDelegateFlowLayout

brycejl
  • 1,411
  • 17
  • 22
3

Swift 3

If you are using swift 3, use this method:

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

    }

Notice the changes:

  1. add _ before collectionView in the method name
  2. NSIndexPath changes to IndexPath
JPetric
  • 3,838
  • 28
  • 26
3

I have the same requirement, in my case below solution is worked. I set UIImageView top, left, bottom and right constraints to 0 inside UICollectionviewCell

@IBOutlet weak var imagesCollectionView: UICollectionView!

override func viewDidLoad() {
    super.viewDidLoad()
    // flowlayout
    let screenWidth = UIScreen.main.bounds.width
    let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
    layout.sectionInset = UIEdgeInsets(top: 5, left: 5, bottom: 10, right: 0)
    layout.itemSize = CGSize(width: screenWidth/3 - 5, height: screenWidth/3 - 5)
    layout.minimumInteritemSpacing = 5
    layout.minimumLineSpacing = 5
    imagesCollectionView.collectionViewLayout = layout

}
Raju Abe
  • 735
  • 2
  • 10
  • 25
0

For my case, I wanted to show two columns and 3 rows in the UICollectionView

I added UICollectionViewDelegateFlowLayout delegate to my class

then I override sizeForItemAt indexPath

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    let cellHeight = (collectionView.bounds.size.height - 30) / 3 // 3 count of rows to show
    let cellWidth = (collectionView.bounds.size.width - 20) / 2 // 2 count of colomn to show
    return CGSize(width: CGFloat(cellWidth), height: CGFloat(cellHeight))
}

The 30 is the Line spacing, the 20 is the insent between cells

Firas Shrourou
  • 625
  • 8
  • 19
-1

Programatically set the itemSize to [UIScreen mainScreen].bounds.size.width/3

rounak
  • 9,217
  • 3
  • 42
  • 59
-1

In my case, assuming every cell has a width of 155 and a height of 220. If I want to show 2 cells per row in a portrait mode and 3 for landscape.

func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
    var itemsCount : CGFloat = 2.0
    if UIApplication.sharedApplication().statusBarOrientation != UIInterfaceOrientation.Portrait {
        itemsCount = 3.0
    }
    return CGSize(width: self.view.frame.width/itemsCount - 20, height: 220/155 * (self.view.frame.width/itemsCount - 20));
}
Eduardo Irias
  • 1,043
  • 11
  • 12
-1

The best solution is to change the itemSize of your UICollectionViewFlowLayout in viewDidLayoutSubviews. That is where you can get the most accurate size of the UICollectionView. You don't need to call invalidate on your layout, that will be done for you already.

shim
  • 9,289
  • 12
  • 69
  • 108
ZaBlanc
  • 4,679
  • 1
  • 35
  • 38
-1

For my case, autolayout was only allowing the cell to grow to its content size even though I overrode the cell size in the storyboard, conformed to UICollectionViewDelegateFlowLayout, and implemented sizeForItemAt. So I made a dummy UIView the width of the cell's bounds.

UICollectionViewController:

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

Cell (I'm calling this method on dependency injection):

private func updateViews() {
    let padding:CGFloat = 8
    let innerView = UIView()
    innerView.translatesAutoresizingMaskIntoConstraints = false
    innerView.layer.cornerRadius = 8
    innerView.layer.borderColor = UIColor.black.cgColor
    innerView.layer.borderWidth = 1
    contentView.addSubview(innerView)
    NSLayoutConstraint.activate([
        innerView.widthAnchor.constraint(equalToConstant: bounds.width - padding*2),
        innerView.leadingAnchor.constraint(equalTo: safeAreaLayoutGuide.leadingAnchor, constant: padding),
        innerView.trailingAnchor.constraint(equalTo: safeAreaLayoutGuide.trailingAnchor, constant: -padding),
        innerView.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor, constant: padding),
        innerView.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor, constant: -padding)
    ])
}
froggomad
  • 1,747
  • 2
  • 17
  • 40