By Suragch


2015-07-30 22:36:28 8 Comments

I'm trying to learn how to use UICollectionView. The documentation is a little hard to understand and the tutorials that I found were either in Objective C or long complicated projects.

When I was learning how to use UITableView, We ❤ Swift's How to make a simple tableview with iOS 8 and Swift had a very basic setup and explanation to get me going. Is there anything like this for UICollectionView?

The answer below is my attempt to learn to do this.

5 comments

@Suragch 2015-07-30 22:36:28

This project has been tested with Xcode 10 and Swift 4.2.

Create a new project

It can be just a Single View App.

Add the code

Create a new Cocoa Touch Class file (File > New > File... > iOS > Cocoa Touch Class). Name it MyCollectionViewCell. This class will hold the outlets for the views that you add to your cell in the storyboard.

import UIKit
class MyCollectionViewCell: UICollectionViewCell {

    @IBOutlet weak var myLabel: UILabel!
}

We will connect this outlet later.

Open ViewController.swift and make sure you have the following content:

import UIKit
class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {

    let reuseIdentifier = "cell" // also enter this string as the cell identifier in the storyboard
    var items = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48"]


    // MARK: - UICollectionViewDataSource protocol

    // tell the collection view how many cells to make
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return self.items.count
    }

    // make a cell for each cell index path
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

        // get a reference to our storyboard cell
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath as IndexPath) as! MyCollectionViewCell

        // Use the outlet in our custom class to get a reference to the UILabel in the cell
        cell.myLabel.text = self.items[indexPath.item]
        cell.backgroundColor = UIColor.cyan // make cell more visible in our example project

        return cell
    }

    // MARK: - UICollectionViewDelegate protocol

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        // handle tap events
        print("You selected cell #\(indexPath.item)!")
    }
}

Notes

  • UICollectionViewDataSource and UICollectionViewDelegate are the protocols that the collection view follows. You could also add the UICollectionViewFlowLayout protocol to change the size of the views programmatically, but it isn't necessary.
  • We are just putting simple strings in our grid, but you could certainly do images later.

Setup the storyboard

Drag a Collection View to the View Controller in your storyboard. You can add constraints to make it fill the parent view if you like.

enter image description here

Make sure that your defaults in the Attribute Inspector are also

  • Items: 1
  • Layout: Flow

The little box in the top left of the Collection View is a Collection View Cell. We will use it as our prototype cell. Drag a Label into the cell and center it. You can resize the cell borders and add constraints to center the Label if you like.

enter image description here

Write "cell" (without quotes) in the Identifier box of the Attributes Inspector for the Collection View Cell. Note that this is the same value as let reuseIdentifier = "cell" in ViewController.swift.

enter image description here

And in the Identity Inspector for the cell, set the class name to MyCollectionViewCell, our custom class that we made.

enter image description here

Hook up the outlets

  • Hook the Label in the collection cell to myLabel in the MyCollectionViewCell class. (You can Control-drag.)
  • Hook the Collection View delegate and dataSource to the View Controller. (Right click Collection View in the Document Outline. Then click and drag the plus arrow up to the View Controller.)

enter image description here

Finished

Here is what it looks like after adding constraints to center the Label in the cell and pinning the Collection View to the walls of the parent.

enter image description here

Making Improvements

The example above works but it is rather ugly. Here are a few things you can play with:

Background color

In the Interface Builder, go to your Collection View > Attributes Inspector > View > Background.

Cell spacing

Changing the minimum spacing between cells to a smaller value makes it look better. In the Interface Builder, go to your Collection View > Size Inspector > Min Spacing and make the values smaller. "For cells" is the horizontal distance and "For lines" is the vertical distance.

Cell shape

If you want rounded corners, a border, and the like, you can play around with the cell layer. Here is some sample code. You would put it directly after cell.backgroundColor = UIColor.cyan in code above.

cell.layer.borderColor = UIColor.black.cgColor
cell.layer.borderWidth = 1
cell.layer.cornerRadius = 8

See this answer for other things you can do with the layer (shadow, for example).

Changing the color when tapped

It makes for a better user experience when the cells respond visually to taps. One way to achieve this is to change the background color while the cell is being touched. To do that, add the following two methods to your ViewController class:

// change background color when user touches cell
func collectionView(_ collectionView: UICollectionView, didHighlightItemAt indexPath: IndexPath) {
    let cell = collectionView.cellForItem(at: indexPath)
    cell?.backgroundColor = UIColor.red
}

// change background color back when user releases touch
func collectionView(_ collectionView: UICollectionView, didUnhighlightItemAt indexPath: IndexPath) {
    let cell = collectionView.cellForItem(at: indexPath)
    cell?.backgroundColor = UIColor.cyan
}

Here is the updated look:

enter image description here

Further study

UITableView version of this Q&A

@Gerald 2016-02-06 09:09:39

im getting cell.myLabel as nil and it's crashing. any idea why? Im using a custom layout

@Suragch 2016-02-06 09:22:14

If you didn't hook up the outlet by control-dragging from the label in the storyboard to the @IBOutlet for myLabel in code, you would get a crash like this.

@Gerald 2016-02-06 09:27:47

@Suragch I have hooked it up already

@Gerald 2016-02-06 13:40:01

if you are using interface builder and reference the cell using an outlet, do not register the custom cell class in your controller. see this

@JamesG 2016-08-26 12:49:29

@Suragch you say: "Hook the Label in the collection cell to myLabel in the MyCollectionViewCell class" how do I add that as a hook up to another class? I am working on the ViewController and I cannot see where to drag the connection

@Suragch 2016-08-26 15:08:25

@JamesG, Don't use the View Controller. Select the MyCollectionViewCell class code. Control drag from the label in the Interface builder to the myLabel outlet in the code.

@Ashok R 2016-09-11 22:00:12

if UICollectionViewCell outlet is nil then you have to remove self.collectionView.registerClass(MyCollectionViewCell.self, forCellWithReuseIdentifier: "Cell"). If you still have the problem check wether reuseIdentifier is same in dequeueReusableCellWithReuseIdentifier and in storyboard

@elmarko 2016-09-25 20:43:45

I wish Apple's documentation was as easy to read as this explanation

@Annabelle Sykes 2016-10-28 17:03:28

Remember that the default backgroundColor of UICollectionView is black.

@Suragch 2016-10-29 00:17:30

@AnnabelleSykes, the default background color for me used to be black when I was making this project in Xcode 7, but most recently when I retested the project in Xcode 8, the default was white. Anyway, whatever the default is for you, it can be changed in the Interface Builder or in code.

@Ilya Vinogradov 2016-10-30 04:13:41

Is it possible to build a static collection view entirely in the Storyboard, similar to how you can build a table view with static content? With static content table view you can completely avoid using cellForRowAtIndexPath:, I was wondering it it's possible to get away without implementing cellForItemAtIndexPath: for my collection view.

@Suragch 2016-10-30 06:45:25

@IlyaVinogradov, I've never done this and a survey of other SO questions seems to indicate that it is not possible (see here and here).

@David DelMonte 2016-11-05 11:23:54

I hope the moderators save this q/a. It's terrific.

@Samball 2017-03-13 08:23:44

Suragch example is a great little example to get started!

@andehlu 2017-05-30 06:04:08

@suragch thank you so much for this! Question - I have a function dynamically creating the data array... how can I call reloadData() for the uicollectionview from my data function?

@Suragch 2017-05-30 10:10:27

@andehlu, I haven't been working with this for a while so I can't remember very well. Off hand I would say load the data in a background thread and when it completes then update the UI with reloadData(). It that doesn't work try asking a new question.

@andehlu 2017-05-30 11:38:57

@Srinivas G 2017-06-10 10:13:59

Cells will repeat if we add more than visible cells. E.g., if I add 1000 in array and try to load in the collection view. While scrolling the collection view, the cells will be repeated. I know its because of reusable identifier, if we want to keep dynamic reuse identifier, then we need to register all reuse identifiers in viewDidLoad and also we have to separate customer cell XIB from storyboard. That look little odd and I feel its not a standard. Do we have any alternate to that solution?

@Suragch 2017-06-10 12:36:33

@SrinivasG, You mean that you made the items array go from 1 to 1000 (["1", "2", ... "999", "1000"]) but it repeated the first screen of numbers (1~70 or so)? I'll make a note to retest this, but if you want a quicker answer you might ask a new question on Stack Overflow.

@Srinivas G 2017-06-12 05:54:52

@Suragch Thanks for checking it out. Yes, the cells will be repeating if custom cell of collection view embedded in storyboard. We were able to get it working properly if the custom cell separated from storyboard as new UICollectionViewCell xib and register dynamic reuse identifiers in viewDidLoad, but that looks odd in registering all identifiers.

@George Kendros 2017-11-17 11:48:18

Thanks for this. Its a great tutorial. It's working perfectly but I'm a tiny bit confused on how it works? The same function "func collectionView" exists 3 times. How does it know which function to run?

@George Kendros 2017-11-17 12:01:37

Sorry, I was looking at it more and think I get it now. When you, for example, link the collection views data source to the file then it looks in there and expects to find at least its mandatory methods (which it tells apart by the parameters)?

@Suragch 2017-11-17 13:49:47

@GeorgeKendros, that is correct. Those are the mandatory methods for the data source and delegate protocols. It tells them apart by the parameters.

@Suragch 2017-12-28 10:29:05

@SrinivasG, I finally got around to testing this. I made the array ["1", "2", ... "99", "100"] so that it scrolled off screen. Everything seemed to work fine. The cells didn't repeat for me. I didn't change anything else about the project besides the data array.

@Exitare 2018-06-20 04:01:20

Great Tutorial! Thanks

@Manu 2018-11-02 10:45:17

@Suragch please help me... I really don't get how, but on first attempt I was able to ctrl-drag the label because I had the class in the "Automatic" section of the "Assitant Editor" but now I only got the viewcontroller class I'm having a burnout

@Abhishek Mishra 2018-11-06 09:07:33

For swift 4.2 --

//MARK: UICollectionViewDataSource

func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
    return 1     //return number of sections in collection view
}

func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return 10    //return number of rows in section
}

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionCell", for: indexPath as IndexPath)
    configureCell(cell: cell, forItemAtIndexPath: indexPath)
    return cell      //return your cell
}

func configureCell(cell: UICollectionViewCell, forItemAtIndexPath: NSIndexPath) {
    cell.backgroundColor = UIColor.black


    //Customise your cell

}

func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {
    let view =  collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "collectionCell", for: indexPath as IndexPath) as UICollectionReusableView
    return view
}

//MARK: UICollectionViewDelegate
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
    // When user selects the cell
}

func collectionView(collectionView: UICollectionView, didDeselectItemAtIndexPath indexPath: NSIndexPath) {
    // When user deselects the cell
}

@Aditya Sharma 2018-09-04 18:38:34

UICollectionView implementation is quite interesting. You can use the simple source code and watch a video tutorial using these links :

https://github.com/Ady901/Demo02CollectionView.git

https://www.youtube.com/watch?v=5SrgvZF67Yw

extension ViewController : UICollectionViewDataSource {

    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 2
    }

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return nameArr.count
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "DummyCollectionCell", for: indexPath) as! DummyCollectionCell
        cell.titleLabel.text = nameArr[indexPath.row]
        cell.userImageView.backgroundColor = .blue
        return cell
    }

}

extension ViewController : UICollectionViewDelegate {

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        let alert = UIAlertController(title: "Hi", message: "\(nameArr[indexPath.row])", preferredStyle: .alert)
        let action = UIAlertAction(title: "OK", style: .default, handler: nil)
        alert.addAction(action)
        self.present(alert, animated: true, completion: nil)
    }

}

@user2613580 2017-12-16 19:02:21

UICollectionView is same as UITableView but it gives us the additional functionality of simply creating a grid view, which is a bit problematic in UITableView. It will be a very long post I mention a link from where you will get everything in simple steps.

@Saranjith 2017-06-22 09:46:00

Delegates and Datasources of UICollectionView

//MARK: UICollectionViewDataSource

override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
    return 1     //return number of sections in collection view
}

override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return 10    //return number of rows in section
}

override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCellWithReuseIdentifier("collectionCell", forIndexPath: indexPath)
    configureCell(cell, forItemAtIndexPath: indexPath)
    return cell      //return your cell
}

func configureCell(cell: UICollectionViewCell, forItemAtIndexPath: NSIndexPath) {
    cell.backgroundColor = UIColor.blackColor()


    //Customise your cell

}

override func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {
    let view =  collectionView.dequeueReusableSupplementaryViewOfKind(UICollectionElementKindSectionHeader, withReuseIdentifier: "collectionCell", forIndexPath: indexPath) as UICollectionReusableView
    return view
}

//MARK: UICollectionViewDelegate
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
      // When user selects the cell
}

override func collectionView(collectionView: UICollectionView, didDeselectItemAtIndexPath indexPath: NSIndexPath) {
     // When user deselects the cell
}

Related Questions

Sponsored Content

41 Answered Questions

[SOLVED] Passing Data between View Controllers

17 Answered Questions

[SOLVED] #pragma mark in Swift?

  • 2014-06-03 14:05:56
  • Arbitur
  • 198595 View
  • 858 Score
  • 17 Answer
  • Tags:   swift

9 Answered Questions

[SOLVED] Swift Beta performance: sorting arrays

22 Answered Questions

[SOLVED] How do I make an attributed string using Swift?

15 Answered Questions

[SOLVED] How to call Objective-C code from Swift

  • 2014-06-02 20:05:42
  • David Mulder
  • 255176 View
  • 870 Score
  • 15 Answer
  • Tags:   objective-c swift

32 Answered Questions

[SOLVED] Split a String into an array in Swift?

90 Answered Questions

22 Answered Questions

[SOLVED] dispatch_after - GCD in Swift?

22 Answered Questions

[SOLVED] @selector() in Swift?

3 Answered Questions

[SOLVED] ios 8 Swift - TableView with embedded CollectionView

Sponsored Content