By Robert Plant


2015-07-23 08:45:45 8 Comments

I am relatively new to iOS programming and have tried a few things but to no avail.

I would like to put a CollectionView inside a TableViewCell. I can code each individually, but dont understand how to set and reference each CollectionView within a TableViewCell.

I have found this tutorial, http://ashfurrow.com/blog/putting-a-uicollectionview-in-a-uitableviewcell/ which shows how it can be done in Objective-C but I have always struggled with Obj-C.

Does anyone know of a Swift Tutorial or can assist? I am in the process of creating a simple project/code which I will post shortly to try and assist.

EDIT 1

I have just found the swift version of the above link. I am working through this now, but seems over complicated, modifying the AppDelegate.

https://github.com/DahanHu/DHCollectionTableView

Many thanks Rob

3 comments

@Vasily Bodnarchuk 2017-03-06 15:07:13

Details

  • Xcode 10.2.1 (10E1001), Swift 5

Full sample

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var tableView: UITableView!
    fileprivate var tableViewCellCoordinator: [Int: IndexPath] = [:]

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.dataSource = self
        tableView.delegate = self
        tableView.tableFooterView = UIView()
    }
}

// UITableViewDataSource

extension ViewController: UITableViewDataSource {

    func numberOfSections(in tableView: UITableView) -> Int {
        return 5
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 10
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "CollectionViewTableViewCell") as! CollectionViewTableViewCell
        cell.selectionStyle = .none
        cell.collectionView.delegate = self
        cell.collectionView.dataSource = self

        let tag = tableViewCellCoordinator.count
        cell.collectionView.tag = tag
        tableViewCellCoordinator[tag] = indexPath

        return cell
    }

    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return "section: \(section)"
    }

}

extension ViewController: UITableViewDelegate {
    func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        let cell = cell as! CollectionViewTableViewCell
        cell.collectionView.reloadData()
        cell.collectionView.contentOffset = .zero
    }
}

// UICollectionViewDataSource

extension ViewController: UICollectionViewDataSource {

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

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

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as! CollectionViewCell

        var text = ""
        if let indexPathOfCellInTableView = tableViewCellCoordinator[collectionView.tag] {
            text = "\(indexPathOfCellInTableView)"
        }
        cell.label.text = text + " \(indexPath)"
        return cell
    }
}

// UICollectionViewDelegate

extension ViewController: UICollectionViewDelegate {

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        print("selected collectionViewCell with indexPath: \(indexPath) in tableViewCell with indexPath: \(tableViewCellCoordinator[collectionView.tag]!)")
    }
}

CollectionViewTableViewCell

import UIKit

class CollectionViewTableViewCell: UITableViewCell {

    @IBOutlet weak var collectionView: UICollectionView!
    @IBOutlet weak var collectionViewFlowLayout: UICollectionViewFlowLayout!
}

CollectionViewCell

import UIKit

class CollectionViewCell: UICollectionViewCell {

    @IBOutlet weak var label: UILabel!
}

Main.storyboard

<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11762" systemVersion="16D32" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
    <device id="retina4_7" orientation="portrait">
        <adaptation id="fullscreen"/>
    </device>
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
        <capability name="Constraints to layout margins" minToolsVersion="6.0"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <scenes>
        <!--View Controller-->
        <scene sceneID="tne-QT-ifu">
            <objects>
                <viewController id="BYZ-38-t0r" customClass="ViewController" customModule="stackoverflow_31582378" customModuleProvider="target" sceneMemberID="viewController">
                    <layoutGuides>
                        <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
                        <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
                    </layoutGuides>
                    <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <subviews>
                            <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="200" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="pS5-CW-ipl">
                                <rect key="frame" x="0.0" y="20" width="375" height="647"/>
                                <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
                                <prototypes>
                                    <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="CollectionViewTableViewCell" id="bMP-Ac-C8D" customClass="CollectionViewTableViewCell" customModule="stackoverflow_31582378" customModuleProvider="target">
                                        <rect key="frame" x="0.0" y="28" width="375" height="200"/>
                                        <autoresizingMask key="autoresizingMask"/>
                                        <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="bMP-Ac-C8D" id="mcy-FO-bcc">
                                            <rect key="frame" x="0.0" y="0.0" width="375" height="199"/>
                                            <autoresizingMask key="autoresizingMask"/>
                                            <subviews>
                                                <collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="yY4-ue-1HX">
                                                    <rect key="frame" x="8" y="8" width="359" height="183"/>
                                                    <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
                                                    <collectionViewFlowLayout key="collectionViewLayout" scrollDirection="horizontal" minimumLineSpacing="10" minimumInteritemSpacing="10" id="pPl-9q-MGc">
                                                        <size key="itemSize" width="180" height="180"/>
                                                        <size key="headerReferenceSize" width="0.0" height="0.0"/>
                                                        <size key="footerReferenceSize" width="0.0" height="0.0"/>
                                                        <inset key="sectionInset" minX="10" minY="0.0" maxX="10" maxY="0.0"/>
                                                    </collectionViewFlowLayout>
                                                    <cells>
                                                        <collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="CollectionViewCell" id="g9z-R1-8XJ" customClass="CollectionViewCell" customModule="stackoverflow_31582378" customModuleProvider="target">
                                                            <rect key="frame" x="10" y="2" width="180" height="180"/>
                                                            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
                                                            <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
                                                                <rect key="frame" x="0.0" y="0.0" width="180" height="180"/>
                                                                <autoresizingMask key="autoresizingMask"/>
                                                                <subviews>
                                                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rHM-Xn-vBW">
                                                                        <rect key="frame" x="69" y="80" width="42" height="21"/>
                                                                        <fontDescription key="fontDescription" type="system" pointSize="17"/>
                                                                        <color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
                                                                        <nil key="highlightedColor"/>
                                                                    </label>
                                                                </subviews>
                                                            </view>
                                                            <color key="backgroundColor" red="0.28627450980000002" green="0.56470588239999997" blue="0.8862745098" alpha="1" colorSpace="calibratedRGB"/>
                                                            <constraints>
                                                                <constraint firstItem="rHM-Xn-vBW" firstAttribute="centerX" secondItem="g9z-R1-8XJ" secondAttribute="centerX" id="AXf-f9-ruf"/>
                                                                <constraint firstItem="rHM-Xn-vBW" firstAttribute="centerY" secondItem="g9z-R1-8XJ" secondAttribute="centerY" id="gw4-Iv-7ML"/>
                                                            </constraints>
                                                            <size key="customSize" width="180" height="180"/>
                                                            <connections>
                                                                <outlet property="label" destination="rHM-Xn-vBW" id="9SL-Kv-ZtD"/>
                                                            </connections>
                                                        </collectionViewCell>
                                                    </cells>
                                                </collectionView>
                                            </subviews>
                                            <constraints>
                                                <constraint firstItem="yY4-ue-1HX" firstAttribute="bottom" secondItem="mcy-FO-bcc" secondAttribute="bottomMargin" id="04L-lF-Idy"/>
                                                <constraint firstItem="yY4-ue-1HX" firstAttribute="leading" secondItem="mcy-FO-bcc" secondAttribute="leadingMargin" id="Fjd-8j-qvK"/>
                                                <constraint firstItem="yY4-ue-1HX" firstAttribute="trailing" secondItem="mcy-FO-bcc" secondAttribute="trailingMargin" id="PUa-ze-U5s"/>
                                                <constraint firstItem="yY4-ue-1HX" firstAttribute="top" secondItem="mcy-FO-bcc" secondAttribute="topMargin" id="XX6-d1-Vgx"/>
                                            </constraints>
                                        </tableViewCellContentView>
                                        <connections>
                                            <outlet property="collectionView" destination="yY4-ue-1HX" id="tLL-Om-JIX"/>
                                            <outlet property="collectionViewFlowLayout" destination="pPl-9q-MGc" id="Ftw-AT-QvP"/>
                                        </connections>
                                    </tableViewCell>
                                </prototypes>
                            </tableView>
                        </subviews>
                        <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                        <constraints>
                            <constraint firstItem="pS5-CW-ipl" firstAttribute="leading" secondItem="8bC-Xf-vdC" secondAttribute="leading" id="3vT-w2-JGU"/>
                            <constraint firstItem="pS5-CW-ipl" firstAttribute="top" secondItem="y3c-jy-aDJ" secondAttribute="bottom" id="eS2-Y5-fxg"/>
                            <constraint firstItem="pS5-CW-ipl" firstAttribute="bottom" secondItem="wfy-db-euE" secondAttribute="top" id="hFA-oB-bWJ"/>
                            <constraint firstAttribute="trailing" secondItem="pS5-CW-ipl" secondAttribute="trailing" id="yin-cp-cAP"/>
                        </constraints>
                    </view>
                    <connections>
                        <outlet property="tableView" destination="pS5-CW-ipl" id="Gfe-HE-Ub6"/>
                    </connections>
                </viewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="136.80000000000001" y="137.18140929535232"/>
        </scene>
    </scenes>
</document>

Results

enter image description here enter image description here

@Kwnstantinos Natsios 2017-03-09 21:53:46

How can i do navigationController?.pushViewController(VC, animated:true) from the UICollectionViewCell that is inside the tableview?

@Vasily Bodnarchuk 2017-03-10 06:41:32

You need to add collectionView.delegate = self in CollectionViewTableViewCell, and extension CollectionViewTableViewCell: UICollectionViewDelegate { func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { //navigationController?.pushViewController(VC, animated:true) } }. P.S. I just edited the answer

@Saeed Rahmatolahi 2018-01-20 10:10:13

my problem is that I want to use the prototype table view cell that has collection view in two cells with different information But I can't get the correct result

@Vasily Bodnarchuk 2018-01-22 08:49:01

Did you try func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return 2 }

@Harshad Patel 2018-06-07 06:01:11

that's working good.. but how can i manage multiple collectionview in multiple tableviewcell...

@Bhadresh Sonani 2018-08-06 17:36:31

i need you help for getting button touchupinside which in collection view with data

@Vasily Bodnarchuk 2018-08-07 23:09:55

So, you can add simple button to UICollectionViewCell.

@Vishal Vaghasiya 2018-10-27 05:15:22

collection view not display

@kurtanamo 2016-02-11 08:30:11

Think about MVC and embedding never programmatically like this above. A subclass of UIView (-> thats what a cell really is) is never the delegate class for a delegate and datasource of an tableView (same for collectionView).

Did you ever create a subclass of tableview to do all the programmatically stuff in this subclass - No! you do it in your viewcontroller - because you create UIView's in Controllers, to "control" them. So the right way you have to do is:

Let me give ya a example (on the oldschool "better-understanding" way):

  1. Create a ViewController with a tableView and add (addSubview) this collectionView to your UITableViewCell.
  2. You have an ViewController on Storyboard and also a UITableView is embedded
  3. It's also connected with your ViewController class as an Outlet (and also his delegate and datasource)
  4. Instead of adding a CollectionView now on your Custom UITableViewCell, just add a "content-holder" UIView (with constraints). Later you will use this view to add a collectionview as an subview.

  5. Now Create a New UIViewController (New File > ...) with XIB OR drag and drop a new UIViewController from properties panel in your storyboard and create also a UIViewController class. Don't forget to connect each other. (we will do the first one for better understanding)

  6. The new ViewController just handle like a ViewController with a CollectionView (same like 1. but collectionView)
  7. In this new ViewController with collectionView you handle everyhting like usual, with delegate and datasource and so on...

  8. NOW: On the ViewController (first one) with tableView you instantiate the new ViewController (with collectionView) on every cell (cellForRowAtIndexPath), and add his collectionView as a subview to the current View you created (as on content holder), e.g.:

let myViewControllerWithCollectionView = MyViewControllerWithCollectionView() myCell.contentHolderView.addSubview(myViewControllerWithCollectionView.collectionView)

What you can also do (and maybe the newer and better way, never tried myself but I'm sure it will work very well, is: UIContainerView).

Thats it! some tips for you:

  • be careful on adding a new subview in cellForRowAtIndexPath, check always if the contentHolderView has already a

    myViewControllerWithCollectionView.collectionView

to get Actions back from the collectionView, to your current View add a custom protocol (delegate) to your view, for more information. Never set delegate and datasource from your collectionView to your main tableViewController, just let handle everything on the right viewcontroller und push information to any other viewcontroller if needed.

@Andrej 2016-03-24 19:02:08

I believe it really makes sense what you're saying, but can you elaborate a little more. What do you mean by "Create a ViewController with a collectionView and add this collectionView to UITableViewCell"? Should this be done in storyboard? How to "add this collectionView to UITableViewCell? Using UIViewContainer?

@kurtanamo 2016-03-25 17:45:46

HI @Andrej maybe this answer will help you...

@Matt 2015-07-23 10:26:02

Create a usual UITableView and in your UITableViewCell create the UICollectionView. Your collectionView delegate and datasource should conform to that UITableViewCell.

Just go through this

In your ViewController

// Global Variable
var tableView: UITableView!

override func viewDidLoad() {
    super.viewDidLoad()

tableView = UITableView(frame: self.view.bounds)
    tableView.delegate = self
    tableView.dataSource = self
    self.view.addSubview(tableView)

    tableView.registerClass(TableViewCell.self, forCellReuseIdentifier: "TableViewCell")
    tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "NormalCell")
}

func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    return 1
}

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return 5
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    if indexPath.row == 3 {
        var cell: TableViewCell = tableView.dequeueReusableCellWithIdentifier("TableViewCell", forIndexPath: indexPath) as! TableViewCell
        cell.backgroundColor = UIColor.groupTableViewBackgroundColor()
        return cell

    } else {
        var cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier("NormalCell", forIndexPath: indexPath) as! UITableViewCell
        cell.textLabel?.text = "cell: \(indexPath.row)"

        return cell
    }
}

As you can see I've created two different cells, a custom TableViewCell which is returned only when the row index is 3 and a basic UITableViewCell in other indices.

The custom "TableViewCell" will have our UICollectionView. So Create a UITableViewCell subclass and write down the below code.

import UIKit

class TableViewCell: UITableViewCell, UICollectionViewDataSource, UICollectionViewDelegate {

var collectionView: UICollectionView!

override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
    super.init(style: style, reuseIdentifier: reuseIdentifier)

    let layout = UICollectionViewFlowLayout()
    layout.scrollDirection = UICollectionViewScrollDirection.Horizontal

    collectionView = UICollectionView(frame: self.bounds, collectionViewLayout: layout)
    collectionView.delegate = self
    collectionView.dataSource = self
    collectionView.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: "CollectionViewCell")
    collectionView.backgroundColor = UIColor.clearColor()

    self.addSubview(collectionView)
}

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

// MARK: UICollectionViewDataSource
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
    return 1
}


func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return 10
}

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
    let cell: UICollectionViewCell = collectionView.dequeueReusableCellWithReuseIdentifier("CollectionViewCell", forIndexPath: indexPath) as! UICollectionViewCell
    if indexPath.row%2 == 0 {
        cell.backgroundColor = UIColor.redColor()
    } else {
        cell.backgroundColor = UIColor.yellowColor()
    }

    return cell
}
}

Hope it helps.

@Robert Plant 2015-07-23 10:49:08

Thanks. I have created the solution and indeed it is exactly what I am after. The amount of code here is far less that anything I have seen posted/on the internet.

@μ4ρκ05 2015-10-04 19:00:06

if I am creating the TableViewCell and the UICollectionView in IB how does the above code change? The only code I need to add from the init() is this: collectionView.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: "CollectionViewCell")? How can I add something to the init of the TableViewCell when it is created in IB?

@Omar Dlhz 2016-04-03 04:08:07

The question is, how do you pass in data from the UITableViewController to the UICollectionView?

@Utku Dalmaz 2016-04-21 22:25:48

@OmarDlhz did you find the answer man?

@Omar Dlhz 2016-04-25 00:37:18

@UtkuDalmaz I did find an answer.

@nmdias 2016-07-27 06:35:49

The UICollectionViewDataSource and UICollectionViewDelegate should not exist in the UITableViewCell's subclass. The view controller should have this responsibility. Look here for an example: ashfurrow.com/blog/…

@kalpesh 2017-02-03 06:10:37

i have done exactly the same but not able to call datasource method of collectionview. just load only the custom cell. plz help me.

@Matt 2017-02-03 06:13:50

You have to show your code. I can't guess.

@Kwnstantinos Natsios 2017-03-09 21:54:25

How can i do navigationController?.pushViewController(VC, animated:true) from the UICollectionViewCell that is inside the tableview?

Related Questions

Sponsored Content

18 Answered Questions

[SOLVED] #pragma mark in Swift?

  • 2014-06-03 14:05:56
  • Arbitur
  • 203124 View
  • 873 Score
  • 18 Answer
  • Tags:   swift

1 Answered Questions

CollectionView with images inside a TableViewCell

1 Answered Questions

0 Answered Questions

I try to do the UICollectionView in a UITableViewCell

0 Answered Questions

UICollectionView inside UITableView

1 Answered Questions

[SOLVED] How do I pass data to a button click event in Swift IOS?

  • 2016-01-12 14:26:29
  • Frederik Hensen
  • 523 View
  • -4 Score
  • 1 Answer
  • Tags:   ios swift

2 Answered Questions

1 Answered Questions

[SOLVED] Logic issues with numberOfItemsInSection:

2 Answered Questions

[SOLVED] How to create UICollectionView without using the storyboard?

Sponsored Content