Ios 8 Swift - TableView with built-in CollectionView

I am relatively new to iOS programming and have tried several things, but to no avail.

I would like to put a CollectionView inside a TableViewCell . I can encode each separately, but I don’t understand how to set and reference each CollectionView within a TableViewCell .

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

Does anyone know about the Swift Tutorial or can it help? I am in the process of creating a simple project / code that I will post in the near future to try to help.

EDIT 1

I just found a quick version of the above link. I am working on it now, but it seems complicated by modifying AppDelegate.

https://github.com/DahanHu/DHCollectionTableView

Thanks a lot Rob

+14
source share
3 answers

Create a regular UITableView and in the UITableViewCell create a UICollectionView. Your collectionView delegate and data source must match this UITableViewCell.

Just go through

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 created two different cells - a custom TableViewCell, which is returned only when the row index is 3 and the base UITableViewCell in other indices.

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

 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 this helps.

+23
source

the 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

+13
source

Think of MVC and embedding, never programmatically similar to the above. The UIView subclass (-> what it really is) is never a delegate class for the delegate and tableView data source (the same for the View collection).

Have you ever created a subclass of tableview to do all the programming stuff in this subclass - No! you do this in your viewcontroller - because you create UIView in controllers to "control" them. So you need to do the right way:

Let me give an example (on the way to a more understandable "old school"):

  • Create a ViewController using tableView and add (addSubview) this Collection ofView to your UITableViewCell.
  • You have a ViewController on a storyboard, as well as a UITableView built-in
  • It is also associated with your ViewController class as an output (and also its delegate and data source).
  • Instead of adding CollectionView now to your custom UITableViewCell, just add the UIView "content holder" (with restrictions). You will use this view later to add the collection as a subquery.

  • Now create a new UIViewController (new file> ...) using XIB OR drag and drop the new UIViewController panel from the property panel in your storyboard and create also the UIViewController class. Do not forget to connect each other. (we will do the first for a better understanding)

  • The new ViewController simply handles as a ViewController with a CollectionView (same as 1. but collectionView).
  • In this new ViewController with collectionView, you handle everything as usual with a delegate and data source, etc.

  • NOW : on the ViewController (first) using tableView you create a new ViewController (using collectionView) on each cell (cellForRowAtIndexPath) and add its collectionView as a subview to the current view that you created (as the owner of the content), for example :.

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

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

Here it is! Some tips for you:

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

    myViewControllerWithCollectionView.collectionView

To return actions from the View collection, add a custom protocol (delegate) to your view for the current view for more information. Never set the delegate and data source from your collection item to your main TableViewController, just let it process everything on the right view manager and display information on any other view manager, if necessary.

+4
source

All Articles