63

I have a UICollectionView with a segmented control to switch between data. But how do I change the UICollectionViewCell size properties programmatically ? then I can custom each style appropriately for each data type.

For example change the width to 520 points and height to 100. I am new to iOS development and am experienced with CSS :/

I know how to do it in Xcode (obviously) but do not know the correct methods to change it in swift. Any ideas or feedback is much appreciated (Y)

enter image description here

RileyDev
  • 3,110
  • 3
  • 18
  • 49

9 Answers9

112

Make sure you conform to UICollectionViewDelegateFlowLayout for your class (for swift4)

eg. class MyClass: UICollectionViewDelegateFlowLayout

Prior to Swift 3

//Use for size
func collectionView(collectionView: UICollectionView,
        layout collectionViewLayout: UICollectionViewLayout,
        sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {

    }
//Use for interspacing
    func collectionView(collectionView: UICollectionView,
        layout collectionViewLayout: UICollectionViewLayout,
        minimumInteritemSpacingForSectionAtIndex section: Int) -> CGFloat {
            return 1.0
    }

    func collectionView(collectionView: UICollectionView, layout
        collectionViewLayout: UICollectionViewLayout,
        minimumLineSpacingForSectionAtIndex section: Int) -> CGFloat {
            return 1.0
    }

// For swift 3

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

}

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

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

// For swift 4

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

        }

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

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

use the same code as shown above for swift 3 but just make sure your class conforms to protocol UICollectionViewDelegateFlowLayout

Sumit Oberoi
  • 3,196
  • 1
  • 16
  • 18
  • 4
    Great thanks ! I have done it and it works great, i simply declared `var size = CGSize(width: 300, height: 100) ` and returned the size property. Unsure how to add a border radius thought, will look into that. Of course this isn't a long term solution need to make the width appropriate for each device. – RileyDev Jul 27 '15 at 20:27
  • 6
    Interestingly there is no auto completion for this method in Xcode 7.3.1. But if you just copy and paste, it works... – Chris Jun 04 '16 at 10:15
66

Swift 3

Methods from @sumit-oberoi answer won't be called if you are using Swift 3. To make it work make this adjustments:

  1. Conform to the UICollectionViewDelegateFlowLayout protocol.

  2. Implement these methods:

    func collectionView(_ collectionView: UICollectionView,
                        layout collectionViewLayout: UICollectionViewLayout,
                        sizeForItemAt indexPath: IndexPath) -> CGSize {
    
    }
    
    func collectionView(_ collectionView: UICollectionView,
                        layout collectionViewLayout: UICollectionViewLayout,
                        minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        return 1.0
    }
    
    func collectionView(_ collectionView: UICollectionView, layout
        collectionViewLayout: UICollectionViewLayout,
                        minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return 1.0
    }
    

Notice the changes:

  1. add _ before collectionView in the method name
  2. NSIndexPath changes to IndexPath
JPetric
  • 3,540
  • 25
  • 24
41

Swift 3

override func viewDidLoad() {
    super.viewDidLoad()

    let cellSize = CGSize(width:80 , height:80)

    let layout = UICollectionViewFlowLayout()
    layout.scrollDirection = .vertical //.horizontal
    layout.itemSize = cellSize
    layout.sectionInset = UIEdgeInsets(top: 1, left: 1, bottom: 1, right: 1)
    layout.minimumLineSpacing = 1.0
    layout.minimumInteritemSpacing = 1.0
    myCollectionView.setCollectionViewLayout(layout, animated: true)

    myCollectionView.reloadData()
}
Gaurav Rami
  • 1,247
  • 14
  • 21
  • This was a great answer for me because I didn't subclass `UICollectionController`, but rather was accessing the Outlet from a standard `UIViewController`. When you try inherit from UIViewController and conform to the protocol UICollectionViewDelegateFlowLayout (`UIViewController, UICollectionViewDelegateFlowLayout`) the compiler complains. – Peza Apr 25 '18 at 17:30
9

if you use Gaurav Rami's code bellow

let cellSize = CGSize(width:80 , height:80)

let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .vertical //.horizontal
layout.itemSize = cellSize
layout.sectionInset = UIEdgeInsets(top: 1, left: 1, bottom: 1, right: 1)
layout.minimumLineSpacing = 1.0
layout.minimumInteritemSpacing = 1.0
myCollectionView.setCollectionViewLayout(layout, animated: true)

and get this message "Snapshotting a view that has not been rendered results in an empty snapshot. Ensure your view has been rendered at least once before snapshotting or snapshot after screen updates." you should remove animated when setting view layout for collection view

myCollectionView.setCollectionViewLayout(layout, animated: false)

then the message will disappear

Heo Đất Hades
  • 1,285
  • 16
  • 12
2

Try to use UICollectionViewDelegateFlowLayout method. In Xcode 11 or later, you need to set Estimate Size to none from storyboard.

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: 
UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
  let padding: CGFloat =  170
  let collectionViewSize = advertCollectionView.frame.size.width - padding
  return CGSize(width: collectionViewSize/2, height: collectionViewSize/2)
}
1
  1. In Storyboard Select the Collection View and Set Estimate Size property as NONE in Size Inspector like this:

this

  1. In View Controller add the UICollectionViewDelegateFlowLayout for View Controller
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    let itemsPerRow:CGFloat = 2
    let padding:CGFloat = 20
    let itemWidth = (collectionView.bounds.width / itemsPerRow) - padding:CGFloat
    let itemHeight = collectionView.bounds.height - (2 * padding:CGFloat)
    return CGSize(width: itemWidth, height: itemHeight)
}
David Buck
  • 3,439
  • 29
  • 24
  • 31
M Praneeth
  • 11
  • 2
0

Swift 5:

  1. Conform to the UICollectionViewDelegateFlowLayout protocol, it means you need to put that protocol in the inheritances of your class or as a extension. Like this:

    class ViewController: UIViewController, UICollectionViewDelegate, 
    UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
    }
    
  2. Implement the sizeForItemAt method:

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { return CGSize(width: 100, height: 100) }

You can put the width and height you want in the CGSize constructor.

0
     /// this method is used for flow layout
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    let numberOfItemsPerRow:CGFloat = 3
    let spacingBetweenCells:CGFloat = 2

    // 2 is the value of sectionInset value
    
    let totalSpacing = (2 * 2) + ((numberOfItemsPerRow - 1) * spacingBetweenCells) //Amount of total spacing in a row
    if let collection = self.collectionAccounts{
        let width = (collection.bounds.width - totalSpacing)/numberOfItemsPerRow
        
        return CGSize(width: width, height: width)
    }else{
        return CGSize(width: 0, height: 0)
    }
}
manishkumar
  • 543
  • 2
  • 5
  • 21
-2
first Conform protocol the UICollectionViewDelegateFlowLayout

set width and height  UICollectionViewCell 

UIcollectionViewCell Hight = 300;
UIcollectionViewCell Width = 380;

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout,sizeForItemAt indexPath: IndexPath) -> CGSize {
      return CGSize(width: CGFloat((380)), height: CGFloat(300))
    }
Ananda Aiwale
  • 261
  • 2
  • 3
  • This is not all code - it doesn't help anyone if you format the entire content as code. Only code should be formatted as code. As it stands, your contribution is not useful. You should also describe what your contribution supplies that others' do not. – Cindy Meister Apr 23 '18 at 17:54