How to add UICollectionView inside UITableViewCell using Swift

Today, I’m going to show you how to add a UICollectionView inside UITableView.

In this example, we have categories, and inside the categories we have a different number of subcategories.

Each subcategory has a UICollectionView and shows the items horizontally.

Every item(UICollectionViewCell) has a UIView to show the color and a UILabel for the name of the color.

Prepare the data

Before we start, create a swift file with a name Colors and paste the following data we’re going to use in this example.

import Foundation import UIKit struct Colors { var objectsArray = [ TableViewCellModel( category: "Category #1", subcategory: ["SubCategory #1.1", "SubCategory #1.2"], colors: [ // SubCategory #1.1 [CollectionViewCellModel(color: UIColor.colorFromHex("#DA70D6"), name: "Orchid"), CollectionViewCellModel(color: UIColor.colorFromHex("#FA8072"), name: "Salmon"), CollectionViewCellModel(color: UIColor.colorFromHex("#FDF5E6"), name: "Old Lace"), CollectionViewCellModel(color: UIColor.colorFromHex("#00FFFF"), name: "Aqua"), CollectionViewCellModel(color: UIColor.colorFromHex("#2E8B57"), name: "Sea Green")], // SubCategory #1.2 [CollectionViewCellModel(color: UIColor.colorFromHex("#2F4F4F"), name: "Dark Slate Gray"), CollectionViewCellModel(color: UIColor.colorFromHex("#F0FFF0"), name: "Honeydew"), CollectionViewCellModel(color: UIColor.colorFromHex("#DCDCDC"), name: "Gainsboro")] ]), TableViewCellModel( category: "Category #2", subcategory: ["SubCategory #2.1"], colors: [ // SubCategory #2.1 [CollectionViewCellModel(color: UIColor.colorFromHex("#FFE4B5"), name: "Moccasin"), CollectionViewCellModel(color: UIColor.colorFromHex("#AFEEEE"), name: "Pale Turquoise"), CollectionViewCellModel(color: UIColor.colorFromHex("#9400D3"), name: "Dark Violet"), CollectionViewCellModel(color: UIColor.colorFromHex("#3CB371"), name: "Medium Sea Green")] ]), TableViewCellModel( category: "Category #3", subcategory: ["SubCategory #3.1", "SubCategory #3.2"], colors: [ // SubCategory #3.1 [CollectionViewCellModel(color: UIColor.colorFromHex("#FF6347"), name: "Tomato"), CollectionViewCellModel(color: UIColor.colorFromHex("#4682B4"), name: "Steel Blue"), CollectionViewCellModel(color: UIColor.colorFromHex("#778899"), name: "Light Slate Gray"), CollectionViewCellModel(color: UIColor.colorFromHex("#191970"), name: "Midnight Blue"), CollectionViewCellModel(color: UIColor.colorFromHex("#A52A2A"), name: "Brown")], // SubCategory #3.2 [CollectionViewCellModel(color: UIColor.colorFromHex("#FFF8DC"), name: "Cornsilk"), CollectionViewCellModel(color: UIColor.colorFromHex("#FF00FF"), name: "Magenta"), CollectionViewCellModel(color: UIColor.colorFromHex("#7CFC00"), name: "Lawn Green"), CollectionViewCellModel(color: UIColor.colorFromHex("#000000"), name: "Black"), CollectionViewCellModel(color: UIColor.colorFromHex("#00BFFF"), name: "Deep Sky Blue"), CollectionViewCellModel(color: UIColor.colorFromHex("#6495ED"), name: "Cornflower Blue"), CollectionViewCellModel(color: UIColor.colorFromHex("#FF8C00"), name: "Dark Orange"), CollectionViewCellModel(color: UIColor.colorFromHex("#20B2AA"), name: "Light Sea Green"), CollectionViewCellModel(color: UIColor.colorFromHex("#FFC0CB"), name: "Pink")] ]), TableViewCellModel( category: "Category #4", subcategory: ["SubCategory #4.1", "SubCategory #4.2"], colors: [ // SubCategory #4.1 [CollectionViewCellModel(color: UIColor.colorFromHex("#DDA0DD"), name: "Plum"), CollectionViewCellModel(color: UIColor.colorFromHex("#FFF5EE"), name: "Seashell"), CollectionViewCellModel(color: UIColor.colorFromHex("#FFDEAD"), name: "Navajo White"), CollectionViewCellModel(color: UIColor.colorFromHex("#00FF00"), name: "Lime"), CollectionViewCellModel(color: UIColor.colorFromHex("#F0E68C"), name: "Khaki")], // SubCategory #4.2 [CollectionViewCellModel(color: UIColor.colorFromHex("#FAEBD7"), name: "Antique White"), CollectionViewCellModel(color: UIColor.colorFromHex("#C71585"), name: "Medium Violet Red"), CollectionViewCellModel(color: UIColor.colorFromHex("#6B8E23"), name: "Olive Drab"), CollectionViewCellModel(color: UIColor.colorFromHex("#FF4500"), name: "Orange Red"), CollectionViewCellModel(color: UIColor.colorFromHex("#FFF0F5"), name: "Lavender Blush")] ]), TableViewCellModel( category: "Category #5", subcategory: ["SubCategory #5.1", "SubCategory #5.2"], colors: [ // SubCategory #5.1 [CollectionViewCellModel(color: UIColor.colorFromHex("#9966CC"), name: "Amethyst")], // SubCategory #5.2 [CollectionViewCellModel(color: UIColor.colorFromHex("#7B68EE"), name: "Medium Slate Blue"), CollectionViewCellModel(color: UIColor.colorFromHex("#800000"), name: "Maroon"), CollectionViewCellModel(color: UIColor.colorFromHex("#FFA07A"), name: "Light Salmon"), CollectionViewCellModel(color: UIColor.colorFromHex("#E6E6FA"), name: "Lavender"), CollectionViewCellModel(color: UIColor.colorFromHex("#FFE4C4"), name: "Bisque")] ]) ] } extension UIColor { static func rgbColor(red: CGFloat, green: CGFloat, blue: CGFloat) -> UIColor { return UIColor(red: red / 255, green: green / 255, blue: blue / 255, alpha: 1.0) } static func colorFromHex(_ hex: String) -> UIColor { var hexString = hex.trimmingCharacters(in: .whitespacesAndNewlines).uppercased() if hexString.hasPrefix("#") { hexString.remove(at: hexString.startIndex) } if hexString.count != 6 { return UIColor.magenta } var rgb: UInt64 = 0 Scanner(string: hexString).scanHexInt64(&rgb) return UIColor(red: CGFloat((rgb & 0xFF0000) >> 16) / 255, green: CGFloat((rgb & 0x00FF00) >> 8) / 255, blue: CGFloat(rgb & 0x0000FF) / 255, alpha: 1.0) } }
Code language: Swift (swift)

Creating the TableView

Add an UITableView in your ViewController. In this example, the name of this ViewController is called TableView.

…and give the name tableView

Creating the TableView Cell

Create an UITableViewCell with XIB file.

Inside the UITableViewCell add an UILabel, which will be the title for the Subcategories and an UICollectionView for the items.

Add tableviewcellid as identifier

Creating the CollectionView Cell

Create an UICollectionViewCell with XIB file.

Add an UIView for the color and an UILabel for the color name.

Add collectionviewcellid as identifier

Creating the Models

Create a new swift file and give the name TableViewCellModel. This will be the model for the TableViewCell.

…and paste the following code inside.

struct TableViewCellModel { var category: String var subcategory: [String] var colors: [[CollectionViewCellModel]] }
Code language: Swift (swift)

Do the same for the CollectionViewCell, giving the name CollectionViewCellModel

import Foundation import UIKit struct CollectionViewCellModel { var color: UIColor var name: String }
Code language: Swift (swift)

Setting up the TableView

Go back to the TableView and create an extension for UITableViewDelegate and UITableViewDataSource.

In this example, row’s height is 200, and header’s 44.

import UIKit class TableView: UIViewController { @IBOutlet var tableView: UITableView! var colorsArray = Colors() override func viewDidLoad() { super.viewDidLoad() // Register the xib for tableview cell let cellNib = UINib(nibName: "TableViewCell", bundle: nil) self.tableView.register(cellNib, forCellReuseIdentifier: "tableviewcellid") } } extension TableView: UITableViewDelegate, UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return colorsArray.objectsArray[section].subcategory.count } func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return 200 } // Category Title func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { let headerView = UIView() headerView.backgroundColor = UIColor.green let titleLabel = UILabel(frame: CGRect(x: 8, y: 0, width: 200, height: 44)) headerView.addSubview(titleLabel) titleLabel.textColor = UIColor.white titleLabel.font = UIFont.systemFont(ofSize: 20, weight: .bold) titleLabel.text = colorsArray.objectsArray[section].category return headerView } func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return 44 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { if let cell = tableView.dequeueReusableCell(withIdentifier: "tableviewcellid") as? TableViewCell { // Show SubCategory Title let subCategoryTitle = colorsArray.objectsArray[indexPath.section].subcategory cell.subCategoryLabel.text = subCategoryTitle[indexPath.row] // Pass the data to colletionview inside the tableviewcell let rowArray = colorsArray.objectsArray[indexPath.section].colors[indexPath.row] cell.updateCellWith(row: rowArray) return cell } return UITableViewCell() } }
Code language: Swift (swift)

Setting up the TableView Cell

On the TableViewCell, declare a variable with a name row and a type [CollectionViewCellModel], which’ll be used to pass the data from the TableViewCell to the collectionView inside the cell.

Also create the flow layout of the collectionView programmatically.

class TableViewCell: UITableViewCell { var row: [CollectionViewCellModel]? override func awakeFromNib() { super.awakeFromNib() let flowLayout = UICollectionViewFlowLayout() flowLayout.scrollDirection = .horizontal flowLayout.itemSize = CGSize(width: 150, height: 180) flowLayout.minimumLineSpacing = 2.0 flowLayout.minimumInteritemSpacing = 5.0 self.collectionView.collectionViewLayout = flowLayout self.collectionView.showsHorizontalScrollIndicator = false // ... } }
Code language: Swift (swift)

In this example, the cell is 150 width and 180 height (150×150 is the box(UIView) for the color and 150×30 the size of the UILabel for the color name)

Set the dataSource and delegate for the collectionView and register the XIB for the CollectionViewCell

class TableViewCell: UITableViewCell { override func awakeFromNib() { super.awakeFromNib() // ... // Comment if you set Datasource and delegate in .xib self.collectionView.dataSource = self self.collectionView.delegate = self // Register the xib for collection view cell let cellNib = UINib(nibName: "CollectionViewCell", bundle: nil) self.collectionView.register(cellNib, forCellWithReuseIdentifier: "collectionviewcellid") } }
Code language: Swift (swift)

Create an extension of the TableViewCell for UICollectionViewDelegate, UICollectionViewDataSource and UICollectionViewDelegateFlowLayout

class TableViewCell: UITableViewCell { var rowWithColors: [CollectionViewCellModel]? // ... } extension TableViewCell: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { // The data we passed from the TableView send them to the CollectionView Model func updateCellWith(row: [CollectionViewCellModel]) { self.rowWithColors = row self.collectionView.reloadData() } func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return self.rowWithColors?.count ?? 0 } func numberOfSections(in collectionView: UICollectionView) -> Int { return 1 } // Set the data for each cell (color and color name) func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionviewcellid", for: indexPath) as? CollectionViewCell { cell.colorView.backgroundColor = self.rowWithColors?[indexPath.item].color ?? UIColor.black cell.nameLabel.text = self.rowWithColors?[indexPath.item].name ?? "" return cell } return UICollectionViewCell() } // Add spaces at the beginning and the end of the collection view func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { return UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5) } }
Code language: Swift (swift)

Detecting which cell is tapped and passing the data

To detect which cell is tapped, go to TableViewCell and create a protocol with a name CollectionViewCellDelegate and inside there add a method that returns the cell (CollectionViewCell), the position(index) of this cell, and which TableViewCell is tapped.

Also declare the protocol CollectionViewCellDelegate as cellDelegate in your TableViewCell class

protocol CollectionViewCellDelegate: class { func collectionView(collectionviewcell: CollectionViewCell?, index: Int, didTappedInTableViewCell: TableViewCell) // other delegate methods that you can define to perform action in viewcontroller } class TableViewCell: UITableViewCell { weak var cellDelegate: CollectionViewCellDelegate? // ... }
Code language: Swift (swift)

In the extension of the TableViewCell you created earlier, add the method didSelectItemAt

extension TableViewCell: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { // ... func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { let cell = collectionView.cellForItem(at: indexPath) as? CollectionViewCell self.cellDelegate?.collectionView(collectionviewcell: cell, index: indexPath.item, didTappedInTableViewCell: self) } // ... }
Code language: Swift (swift)

Every time the user presses a cell, it sends the data through the protocol.

To receive that data in the TableView (the ViewController with the UITableView), go back to the TableView and set the delegate of the cell (cellDelegate) inside the cellForRowAt method.

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { if let cell = tableView.dequeueReusableCell(withIdentifier: "tableviewcellid") as? TableViewCell { // ... // Set cell's delegate cell.cellDelegate = self // ... return cell } return UITableViewCell() }
Code language: Swift (swift)

Lastly, create an extension for this delegate to receive the data.

extension TableView: CollectionViewCellDelegate { func collectionView(collectionviewcell: CollectionViewCell?, index: Int, didTappedInTableViewCell: TableViewCell) { if let colorsRow = didTappedInTableViewCell.rowWithColors { print("You tapped the cell \(index) in the row of colors \(colorsRow)") // You can also do changes to the cell you tapped using the 'collectionviewcell' } } }
Code language: Swift (swift)

That’s it!

You can find the final project here

If you have any questionsplease feel free to leave a comment below

Subscribe
Notify of
guest
7 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Melih

Hi, thanks for the amazing blog, one question though, once done as said, it gives plenty of errors stating rowWithColors is not defined. Checking your code above there is no reference to it prior to mentioning in TableViewCell Extension. Am I missing something ?

ref.coder

Hi, Thank You for u’r tutorial, i really appreciate your help.

ben

wow! Brilliant effort from your side, Explained step by step and the presentation is ordered well 🙂

cindy

Thank you for sharing your knowledge! Could you provide some information on how we should go about deleting a cell? Do we delete within the table view or collection view? Thanks!

cindy

Oh! I should have clarified, sorry about that…I would like the ability to delete an item or multiple items from the collection view. Is it better to have a delete button inside the detail view controller or select multiple items from the collection view?