How to create custom cells for UICollectionView using Swift

Let’s continue our journey through the UICollectionView class. Creating cells for UICollectionView is really easy, moreover you can use the same approach for UITableView. Let’s take a look at how to do it using Swift.

You will learn how to create simple cells with text and images in them, how to handle taps, and at the end of the article I’ll talk about how to use a xib file to create a cell.

In this tutorial I presume that you’ve already finished my previous tutorial — How to create UICollectionView using Swift without storyboards, because here I’ll use our finished Xcode project. If you didn’t, then don’t worry, the only thing you need here is working UICollectionView.

So, in previous tutorial we used just a UICollectionViewCell. It’s the cell that represents an empty view. The view that has no subviews at all.

If you want to add something to it like labels or images, you have to make a subclass of UICollectionViewCell.


    1. Select FileNewFile… or just press ⌘N.
    2. Choose Source under iOS on the left side of the window. Select Cocoa Touch Class and click Next.
    3. Name your cell class whatever you like. I’ll name it RDCell. In Subclass field select (you can also write here) UICollectionViewCell. We don’t need a XIB file, you probably know why 😉 So make sure that Also create XIB file option is unchecked. Choose Swift as a language for this class.
    4. Click Next, and then Create to create a new class.

This is the class of our cell. Before we add something to it, let’s set this class as a cell for our UICollectionView. Go to ViewController.swift file, where we have our UICollectionView set up. Replace this piece of code

collectionView!.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell")

with this:

collectionView!.registerClass(RDCell.self, forCellWithReuseIdentifier: "Cell")

If your cell class name is different from mine (not RDCell), then wherever I write RDCell you write the name of your cell class.
As you can see, we didn’t import anything, unlike in Objective-C. This is because here in Swift every file knows about any class in the project, so the only one thing that you import is frameworks like UIKit or QuartzCore.

We need to do one little thing before we begin working with the cell. Replace this piece of code

let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath)

with this:

let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! RDCell

So now the cell variable is of type RDCell, not UICollectionViewCell. This is important, because now we can get access to RDCell’s properties and methods.

The Cell

Open RDCell.swift file (in my case. In yours it’s the file in which you have your new cell class).
Here we’ll do all the magic. Because this article is about simple cells, we’ll create a cell with just a label and an image view, to tell you how to put pictures and text to a cell.

So we need a UILabel and a UIImageView objects to add to our cell. Add two new variables to RDCell class.

var textLabel: UILabel!
var imageView: UIImageView!

Alright. Let’s initialize them.

override init(frame: CGRect) {
    super.init(frame: frame)
    imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height*2/3))
    imageView.contentMode = UIViewContentMode.ScaleAspectFit
    textLabel = UILabel(frame: CGRect(x: 0, y: imageView.frame.size.height, width: frame.size.width, height: frame.size.height/3))
    textLabel.font = UIFont.systemFontOfSize(UIFont.smallSystemFontSize())
    textLabel.textAlignment = .Center

If you take a look at the frames of image view and label, you’ll see that the image view will take 2/3 of the whole space in the cell. And label will take the rest 1/3. We’ve set imageView.contentMode property to .ScaleAspectFit, so any image that you pass to image view will be scaled to fit the image view frame, and an image’s aspect ratio will be saved.
Ok, we’re done but here we have some errors. Let’s fix ’em.

In Xcode 6 you have to provide additional init(coder:) initializer in classes like RDCell, which is the subclass of UICollectionViewCell. This initializer is called instead of init(frame:) when the class gets initialized from a storyboard or a xib file. That’s not our case, but we still need to provide init(coder:). We can use the solution provided to us by Xcode. In Issue Navigator click on an error that says “'required' initializer 'init(coder:)' must be provided by subclass of 'UICollectionViewCell'“, and then press Return or double-click on a “Fix-it Insert …” row that will appear. You can also click on a red circle with a white dot on a vertical bar to the left of your code where line numbers are, and do the same thing.
Ok, it automatically pasted to our class this piece of code:

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")

Well, “Fatal error” sounds creepy. But nothing to worry about, because this initializer in our case never going to be called.

Exclamation marks in our variables declarations after a class name means that this object is implicitly unwrapped optional. This is pretty dangerous, because if you try to work with an object before it’s initialized, your app will crash. So why are we doing that here?
The thing is, in Swift all non-optional variables must be initialized before calling superclass’ initializer. If you place an exclamation mark after a class name, Swift will think that this variable is already initialized, and no error will occur. So I did it to just simplify our code in init(frame:). If you want, you can remove exclamation marks, and then you need to place text label and image view initialization code before super.init(frame: frame), like this:

override init(frame: CGRect) {
    imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height*2/3))
    textLabel = UILabel(frame:  CGRect(x: 0, y: imageView.frame.size.height, width: frame.size.width, height: frame.size.height/3))
    super.init(frame: frame)
    // ... and then the rest of the code

To continue, you’ll need an image to set to our cell imageView.image. You can use mine. It needs to be named "star@2x.png". After you download the image, drag and drop it to the Xcode project and check Copy items if needed, then click Finish.


Alright, so now we have ready-to-use cell, let’s set some text to it and the image so we’ll see the result.
Go to ViewController.swift and edit collectionView(_:cellForItemAtIndexPath:) method:

let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! RDCell
cell.textLabel.text = "Text"
cell.imageView.image = UIImage(named: "star")
return cell

That’s it. Run the app by pressing ⌘R and you’ll see the same number of cells but with a star and a text in it.

So this is how you create a simple UICollectionViewCell and use it. Don’t forget to experiment with it! For example, try to create an array of strings and/or image names and pass each one for each cell.

UICollectionViewCell with XIB

If you want, you can create cells in the Interface Builder. When you add new UICollectionViewCell subclass, check Also create XIB file and Xcode will create a xib file for you.
If you already created a UICollectionViewCell subclass and you want to create a xib file for it:
1. Select FileNewFile… or press ⌘N, select User Interface under iOS on the left, then select View and click Next. Give that file the same name as your cell class (doesn’t matter though, it’s just to keep things clean).
2. Go to this new xib file and delete the view that’s here.
3. Drag and drop a Collection View Cell from the bottom of the Utilities panel (⌥⌘0).
4. Select this Collection View Cell and switch to Identity inspector on the Utilities panel (⌥⌘3).
5. In Custom Class section in Class field write your custom cell class name (RDCell in my case).
6. Now remove the line where you register your class as a cell and register the nib instead. For example, in our example above you need to replace this line of code:

collectionView!.registerClass(RDCell.self, forCellWithReuseIdentifier: "Cell")

with this:

collectionView!.registerNib(UINib(nibName: "RDCell", bundle: nil), forCellWithReuseIdentifier: "Cell")

Now that’s it. Remember that now you need to recreate our imageView and textLabel in Interface Builder.

The init(frame:) method will not get called now, instead init(coder:) will be called. So if you followed this tutorial, you need to make a change to init(coder:):

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)

Now it won’t crash with the error “init(coder:) has not been implemented“, because now you actually implemented it.

Write @IBOutlet at the beginning of a variable declaration to make it available in the Interface Builder. An IBOutlet object must not be a constant. This means, not let, but var.

@IBOutlet let textLabel: UILabel!

— compiler will be angry if you write this.
All IBOutlets will be available from the Connections Inspector on the Utilities panel, when you select your Collection View Cell.
And finally, if you use xib file and you still need to do some initialization in code, override awakeFromNib() method.

Where to go from here?

Check out the documentation of course. It’s not really rich though, because it’s only up to you what will be in the cell and what it’ll do. Also, take a look at Advanced User Interfaces with Collection Views video from WWDC 2014, it may be useful for you.

If you have a question, suggestion, or if I did something wrong here, please let me now in comments below.

25 replies to “How to create custom cells for UICollectionView using Swift”

    1. The trick is in indexPath parameter in collectionView(_:cellForItemAtIndexPath:) method. I’ll write a post about it, so come back a little later if you don’t figure it out.

      1. I enjoyed this tutorial and topic. I extended the idea to use relative values, so that the layout works across different devices. In addition to creating individual labels, and rather than create and upload 14 (15 in my case) image files, I also use an array with a for in loop to set different cell background colours. I give the labels a semi-opaque background colour to ensure black label text is readable across a range of available standard UIColors (including black). I did however, make my own seasonal star.
        Thanks again.
        P.s. Here is link to a screengrab:

  1. Hi there, I loved the tutorial! I was wondering if you would consider taking it a bit further and say showing how to link it up to display a viewcontroller when different items are selected?

  2. I just want to say thank you! Not only did you help me better understand how to use collections you did it in a way that I felt like I learned the little things along the way. There are tons of tutorials out there that just have you copy in code and for someone who has been coding for a while that may be enough to understand how things work but for someone like me who is new you explain things really well. I just wanted to say thank you and you are awesome sir.

  3. Hey thanks a bunch for this article, very well written and it was exactly what I needed after searching for 2 hours. Keep up the great work, I’ll be bookmarking this site for sure!

  4. Hi! For me I run into this problem: Initializer does not override a designated initializer from its superclass. And additionally, it says I did not initialize textLabel and imageLabel…

  5. Nice article. 🙂 Do you really need to call the cell something like ‘RDCell’, though? I thought we didn’t need these two-character prefixes any more, since Swift has namespacing.

  6. This and the tutorial about programmatic collection views were both well put together and surprisingly easier to comprehend for me than the other CollectionView tutorials I’ve seen that use IB.

  7. Hi there, so I have a strange problem. In the collectionView(_:cellForItemAtIndexPath:) method I can change the background color of each cell, but for some reason I can only set an image for the first cell in the collection view. For the others, the same code runs (cell.imageView!.image = img), but no image gets set. I can say for sure that the img variable is a real image in every case as I tried it with UIImage(named: “something static”).

    Any ideas?
    Thanks, it’s a great tutorial but I’m not sure what I missed.

  8. I don’t know how to thank you enough. I’ve spent 20 hours trying to look through Apple documentation and stackoverflow…and everything was way too advance.

    Your teaching style is patient and thorough, and repeats concepts from earlier, which helps me learn.

Leave a Reply to Euler Cancel reply

Your email address will not be published. Required fields are marked *