I am attempting to set a CollectionView inside of a TableViewCell. I have read through a hand full of stack questions, tuts, and videos and so far I have what appears to be the correct method but my collection view is still not loading into my table view cell.
Code:
import UIKit
class TheaterCell: UITableViewCell {
#IBOutlet var theaterName: UILabel!
#IBOutlet var theaterLocation: UILabel!
#IBOutlet var showtimesCollection: UICollectionView!
override func awakeFromNib() {
super.awakeFromNib()
showtimesCollection.delegate = self
showtimesCollection.dataSource = self
showtimesCollection.reloadData()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
extension TheaterCell: UICollectionViewDataSource, UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = showtimesCollection.dequeueReusableCell(withReuseIdentifier: "timeCell", for: indexPath) as! TimeCell
cell.time.text = "1:00"
return cell
}
}
The Tableview loads from the ViewController and is displaying its cells and elements but the collection view is not loading within the cell.
This is working for me, I think the problems comes from the way you register your cell
class YourCell: UITableViewCell, UICollectionViewDelegate, UICollectionViewDataSource {
#IBOutlet weak var collectionView: UICollectionView!
override func awakeFromNib() {
super.awakeFromNib()
registerCell()
self.collectionView.delegate = self
self.collectionView.dataSource = self
}
func registerCell() {
collectionView.register(TimeCell.self, forCellWithReuseIdentifier: "cell")
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! TimeCell
cell.time.text = "1:00"
return cell
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
What I do, I use storyboard and setting the delegates & datasource from Storyboard by dragging into the classes.
a) Set TableView's delegates & datasource to ViewController
b) Set CollectionView's delegates & datasource to TableViewCell(TheaterCell)
ViewController Code:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
}
extension ViewController:UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let theaterCell:TheaterCell = tableView.dequeueReusableCell(withIdentifier: "TheaterCell", for: indexPath) as! TheaterCell
theaterCell.reloadCollectionView()
return theaterCell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 200
}
}
TheaterCell Code:
class TheaterCell: UITableViewCell {
#IBOutlet var showtimesCollection: UICollectionView!
override func awakeFromNib() {
super.awakeFromNib()
}
func reloadCollectionView() -> Void {
self.showtimesCollection.reloadData()
}
}
extension TheaterCell: UICollectionViewDataSource, UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = showtimesCollection.dequeueReusableCell(withReuseIdentifier: "timeCell", for: indexPath) as! TimeCell
cell.time.text = "1:00"
return cell
}
}
TimeCell Code:
class TimeCell: UICollectionViewCell {
#IBOutlet var time: UILabel!
}
Here is the output:
NOTE: IF YOU ARE NOT USING STORYBOARDS AND YOU MAKE COLLECTIONVIEW OR TABLE FROM CODE ONLY, THEN YOU HAVE REGISTER YOURS CELL AS:
A) TheaterCell must be registered into ViewController class
B) TimeCell must be registered into TheaterCell class
Related
There is a very similar question here, but the solution doesn't solve anything for me, mainly because my embedded collection view is already inside the table view cell's content view (I created it in storyboard).
Is there some setting that I need to check to allow my collection view to scroll? It seems that the parent table view cell is eating up all gestures.
TableViewController.swift
class TableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UICollectionViewDataSource, UICollectionViewDelegate {
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomTableViewCell
cell.collectionView.dataSource = self
cell.collectionView.delegate = self
return cell
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 5
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionCell", for: indexPath)
return cell
}
}
CustomTableViewCell.swift
class CustomTableViewCell: UITableViewCell {
#IBOutlet weak var collectionView: UICollectionView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
You can find my demo project here
https://github.com/MattiaPell/CollectionView-inside-a-TableViewCell
Turns out in my case it was simple as not having User Interaction Enabled check marked for my child collection view.
I am Trying to Create a Table View which is
Expandable
It's Childview has a UICollectionView
UICollectionView is Dynamically rendered using API
onTouch Event of UICollectionView Item should be taken care of
Tried couple of samples didn't work
import UIKit
extension String{
func print(){
Swift.print(self)
}
func log(){
Swift.print(self)
}
}
class CustomVCViewController: UIViewController, UITableViewDataSource, UITableViewDelegate , UICollectionViewDataSource, UICollectionViewDelegate{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 5
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ccell", for: indexPath) as! CustomCollectionViewCell
cell.backgroundColor = UIColor.blue
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
"Selection of Collection View item at \(indexPath.row)".print()
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
"Selection of Table View item at \(indexPath.row)".print()
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell",for: indexPath) as! CustomTableViewCell
cell.innerCollectionView.dataSource = self
cell.innerCollectionView.delegate = self
cell.backgroundColor = UIColor.red
return cell
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
// Do any additional setup after loading the view.
}
}
This is for the table view cell
import UIKit
class CustomTableViewCell: UITableViewCell {
#IBOutlet weak var innerCollectionView: UICollectionView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
According to my understanding you want the tableviewcell to be expandable as collection view so Im adding the code for it , the thing is collectionview is not invalidating its first internsic content size accordingly
/// This CollectionView class is used to invalidate collectionview's default implementation of inrinsic content size
class DynamicCollectionView: UICollectionView {
override func layoutSubviews() {
super.layoutSubviews()
if !__CGSizeEqualToSize(bounds.size, self.intrinsicContentSize) {
self.invalidateIntrinsicContentSize()
}
}
override var intrinsicContentSize: CGSize {
return collectionViewLayout.collectionViewContentSize
}
}
SO add this class to your collectionview and provide flowlayout to your collectionviewcells. also!!! return UITableViewAutomaticDimension on heightforRowAt of tableview.
How can I make a horizontal UICollectionView with pickerView effect? I have already created the horizontal UICollectionView. Now I want to apply the UIPickerView effect on it.
Here is the image where i wanted to make that effect(UIPickerView effect):
Here is my ViewControllerCode -
import UIKit
class WalletViewController: UIViewController,UICollectionViewDataSource, UICollectionViewDelegate {
#IBOutlet var tableView: UICollectionView!
#IBOutlet var walletImage: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
let nibName = UINib(nibName: "WalletCollectionViewCell", bundle: nil)
tableView.register(nibName, forCellWithReuseIdentifier: "walletCollectionViewCell")
}
// MARK: - Table view data source
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 15
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = tableView.dequeueReusableCell(withReuseIdentifier: "walletCollectionViewCell", for: indexPath) as! WalletCollectionViewCell
return cell
}
}
I have a UITableView inside each cell of a UICollectionView.
I referenced the table view to a FeedCell class, which is a class for each cell in the collection view. However, I got an error saying
fatal error: unexpectedly found nil while unwrapping an Optional value
when trying to access the tableView object in the FeedCell class. I triple checked that I referenced the table view from the storyboard correctly, so I'm assuming there's something else that's causing it to be nil, but I'm not sure what it is. Does anyone have a clue about how to fix this?
This is my VC where both the collection view and the table view lies.
class HomeViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate,UICollectionViewDelegateFlowLayout, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.navigationBar.isTranslucent = false
collectionView.register(FeedCell.self, forCellWithReuseIdentifier: "FeedCell")
}
// for feedcell and its collection view
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return images.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "FeedCell", for: indexPath) as! FeedCell
cell.backgroundColor = backgrounds[indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
guard let collectionViewCell = cell as? FeedCell else { return }
collectionViewCell.setTableViewDataSourceDelegate(dataSourceDelegate: self, forRow: indexPath.row)
}
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.width, height: view.frame.height)
}
// conform to tableview protocol for hometable
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return images[tableView.tag].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "HomeCell", for: indexPath)
cell.imageView?.image = images[tableView.tag][indexPath.item]
return cell
}
override func viewWillAppear(_ animated: Bool) {
self.navigationController?.setNavigationBarHidden(true, animated: animated)
super.viewWillAppear(animated)
}
override func viewWillDisappear(_ animated: Bool) {
self.navigationController?.setNavigationBarHidden(false, animated: animated)
super.viewWillDisappear(animated)
}
}
And here's a cell of the collection view where I reference the table view.
class FeedCell: BaseCollectionViewCell{
#IBOutlet weak var tableView: UITableView!
override func setupViews() {
super.setupViews()
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 4
}
}
extension FeedCell {
func setTableViewDataSourceDelegate
<D: UITableViewDataSource & UITableViewDelegate>
(dataSourceDelegate: D, forRow row: Int) {
# ERROR!
tableView.delegate = dataSourceDelegate
tableView.dataSource = dataSourceDelegate
tableView.tag = row
tableView.reloadData()
}
}
On this line:
(dataSourceDelegate: D, forRow row: Int)
You cast it as the variable: D
Use this instead:
tableView.dataSource = D
tableView.delegate = D
I have a custom UITableViewCell class which has a collectionview inside. In this class there's a varaible "nbElements" which contain the number of collectionview cells to display. I want to know how I can initialize the value of nbElements when I create a tableview cell with dequeueReusableCell?
Here is the code of the custom tableview cell:
class CustomTableViewCell: UITableViewCell, UICollectionViewDataSource, UICollectionViewDelegate {
#IBOutlet weak var collectionView: UICollectionView!
#IBOutlet weak var collectionViewHeightConstraint: NSLayoutConstraint!
var nbElements = Int!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
self.collectionView.register(UINib(nibName: "CustomCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "customCollectionViewCell")
self.collectionViewHeightConstraint.constant = self.collectionView.collectionViewLayout.collectionViewContentSize.height
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return nbElements
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "customCollectionViewCell", for: indexPath)
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let cellWidth = (self.collectionView.frame.width / 2.0) - 1
let size = CGSize.init(width: cellWidth, height: cellWidth)
return size;
}
}
Here is the code when I create a tableview cell:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "customTableViewCell", for: indexPath) as! CustomTableViewCell
cell.nbElements = 4
return cell
}
Regards.
Trigger reload of collectionView whenever the nbElements is set.
var nbElements = Int! {
didSet {
collectionView.reloadData()
}
}