multiple UICollectionviews inside a UItabelview with sections - swift - ios

Basically, I am trying to obtain something similar to the result of this : http://ashfurrow.com/blog/putting-a-uicollectionview-in-a-uitableviewcell
how ever, i'm want 2 collectionsViews, 1 in first section and another in 2nd section
how do i achieve this ?
i'm bashing my head in here... each time i try to set delegate and datasource it fails and says i need to register a nib
Also i have no idea how i separate the collection views in it's cellForRowAtIndexPath, how can i see which one is loading ?
how can i get it to load properly so that first section has self.genre and 2nd section has the self.radioStat
this is the error i get when i try to set delegate and datasource for the 2nd time
**Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'could not dequeue a view of kind: UICollectionElementKindCell with identifier CollectionViewCell34 - must register a nib or a class for the identifier or connect a prototype cell in a storyboard'
**
tableViewCellCollectionView.swift
import Foundation
import UIKit
let collectionViewCellIdentifier: NSString = "CollectionViewCell"
class tableViewCellCollectionView: UITableViewCell {
var collectionView: UICollectionView!
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
var layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsetsMake(4, 5, 4, 5)
layout.minimumLineSpacing = 5
layout.itemSize = CGSizeMake(91, 91)
layout.scrollDirection = UICollectionViewScrollDirection.Horizontal
self.collectionView = UICollectionView(frame: CGRectZero, collectionViewLayout: layout)
self.collectionView.registerClass(collectionViewCellTableView.self, forCellWithReuseIdentifier: "CollectionViewCell")
self.collectionView.registerClass(collectionViewCellTableView.self, forCellWithReuseIdentifier: collectionViewCellIdentifier)
self.collectionView.backgroundColor = UIColor.lightGrayColor()
self.collectionView.showsHorizontalScrollIndicator = false
self.contentView.addSubview(self.collectionView)
self.layoutMargins = UIEdgeInsetsMake(10, 0, 10, 0)
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func layoutSubviews() {
super.layoutSubviews()
let frame = self.contentView.bounds
self.collectionView.frame = CGRectMake(0, 0.5, frame.size.width, frame.size.height - 1)
}
func setCollectionViewDataSourceDelegate(dataSourceDelegate delegate: protocol<UICollectionViewDelegate,UICollectionViewDataSource>, index: NSInteger) {
self.collectionView.dataSource = delegate
self.collectionView.delegate = delegate
self.collectionView.tag = index
self.collectionView.reloadData()
}
}
tableViewCellCollectionView34.swift
import Foundation
import UIKit
let collectionViewCellIdentifier34: NSString = "CollectionViewCell34"
class tableViewCellCollectionView2: UITableViewCell {
var collectionView2: UICollectionView!
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
var layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsetsMake(4, 5, 4, 5)
layout.minimumLineSpacing = 1
layout.itemSize = CGSizeMake(86, 54)
layout.scrollDirection = UICollectionViewScrollDirection.Vertical
self.collectionView2 = UICollectionView(frame: CGRectZero, collectionViewLayout: layout)
self.collectionView2.registerClass(collectionViewCellTableView2.self, forCellWithReuseIdentifier: collectionViewCellIdentifier34)
self.collectionView2.backgroundColor = UIColor.lightGrayColor()
self.collectionView2.showsHorizontalScrollIndicator = false
self.contentView.addSubview(self.collectionView2)
self.layoutMargins = UIEdgeInsetsMake(10, 0, 10, 0)
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func layoutSubviews() {
super.layoutSubviews()
let frame = self.contentView.bounds
self.collectionView2.frame = CGRectMake(0, 0.5, frame.size.width, frame.size.height - 1)
}
func setCollectionViewDataSourceDelegate2(dataSourceDelegate delegate: protocol<UICollectionViewDelegate,UICollectionViewDataSource>, index: NSInteger) {
self.collectionView2.dataSource = delegate
self.collectionView2.delegate = delegate
self.collectionView2.tag = index
self.collectionView2.registerClass(collectionViewCellTableView2.self, forCellWithReuseIdentifier: collectionViewCellIdentifier34)
self.collectionView2.reloadData()
}
}
Player.swift
extension Player: UICollectionViewDataSource,UICollectionViewDelegate {
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.radioStat.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell: collectionViewCellTableView = collectionView.dequeueReusableCellWithReuseIdentifier(reuseCollectionViewCellIdentifier, forIndexPath: indexPath) as collectionViewCellTableView
var rowData: String = self.genre.objectAtIndex(indexPath.row) as String
cell.title.text = rowData as String
let cell2: collectionViewCellTableView2 = collectionView.dequeueReusableCellWithReuseIdentifier(reuseCollectionViewCellIdentifier2, forIndexPath: indexPath) as collectionViewCellTableView2
let userPost: String = self.radioStat.objectAtIndex(indexPath.row) as String
cell2.pinImage.image = UIImage(named: userPost)
let collectionViewArray = self.sourceArray[collectionView.tag] as NSArray
return cell
}
extension Player {
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier(reuseIdentifier, forIndexPath: indexPath) as UITableViewCell
if (indexPath.section == 0){
var cell2: tableViewCellCollectionView = tableView.dequeueReusableCellWithIdentifier(reuseTableViewCellIdentifier, forIndexPath: indexPath) as tableViewCellCollectionView
return cell2
}else if (indexPath.section == 1){
let cell3: tableViewCellCollectionView2 = tableView.dequeueReusableCellWithIdentifier(reuseTableViewCellIdentifier2, forIndexPath: indexPath) as tableViewCellCollectionView2
return cell3
}
return cell
}
func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
if (indexPath.section == 0){
if let collectionCell: tableViewCellCollectionView = cell as? tableViewCellCollectionView{
collectionCell.setCollectionViewDataSourceDelegate(dataSourceDelegate: self, index: indexPath.row)
}
}
if (indexPath.section == 1){
if let collectionCell2: tableViewCellCollectionView2 = cell as? tableViewCellCollectionView2{
collectionCell2.setCollectionViewDataSourceDelegate2(dataSourceDelegate: self, index: indexPath.row)
}
}
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if (indexPath.section == 0){
return 50
}
if (indexPath.section == 1){
return 450
}else {
return 100
}
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return sourceArray.count
}
}

You need to know which (tableView) section your collectionView relates to. One way to do this would be to subclass UICollectionView and add a tvSection property to it - in the same way that Ash Furrow uses AFIndexedCollectionView to add an index property.
But you seem to be using the collectionView tag in place of his index, presumably to avoid subclassing. If so, a similar dodge would be to use the tag of the tableViewCell's contentView to indicate which section the cell is in. Amend the tableView cellForRowAtIndexPath method to do this:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier(reuseIdentifier, forIndexPath: indexPath) as UITableViewCell
if (indexPath.section == 0){
var cell2: tableViewCellCollectionView = tableView.dequeueReusableCellWithIdentifier(reuseTableViewCellIdentifier, forIndexPath: indexPath) as tableViewCellCollectionView
cell2.contentView.tag = indexPath.section
return cell2
} else if (indexPath.section == 1){
let cell3: tableViewCellCollectionView2 = tableView.dequeueReusableCellWithIdentifier(reuseTableViewCellIdentifier2, forIndexPath: indexPath) as tableViewCellCollectionView2
cell3.contentView.tag = indexPath.section
return cell3
}
cell.contentView.tag = indexPath.section
return cell
}
Because the collectionView is added as a subview of the contentView, you can determine the (tableView) section for a given collectionView using superview.tag. So modify your collectionView's cellForItemAtIndexPath to test this:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let section = collectionView.superview.tag
if section == 0 {
let cell: collectionViewCellTableView = collectionView.dequeueReusableCellWithReuseIdentifier(reuseCollectionViewCellIdentifier, forIndexPath: indexPath) as collectionViewCellTableView
// Now configure from the data...
var rowData: String = self.genre.objectAtIndex(indexPath.row) as String
cell.title.text = rowData as String
return cell
else {
let cell2: collectionViewCellTableView2 = collectionView.dequeueReusableCellWithReuseIdentifier(reuseCollectionViewCellIdentifier2, forIndexPath: indexPath) as collectionViewCellTableView2
// Now configure from the data...
let userPost: String = self.radioStat.objectAtIndex(indexPath.row) as String
cell2.pinImage.image = UIImage(named: userPost)
return cell2
}
}
This will need some polishing; in particular I'm not sure how you wish to map your data (sourceArray, genre and radioStat) to the tableView rows and collectionView items. (I've taken a guess based on your existing code). But this should give you something to work with.

Related

cells must be retrieved by calling - dequeueReusableCellWithReuseIdentifier:forIndexPath

Problem encountered:
the cell returned from -collectionView:cellForItemAtIndexPath: does not have a reuseIdentifier - cells must be retrieved by calling - dequeueReusableCellWithReuseIdentifier:forIndexPath:"
In my TableViewCell, I have a CollectionViewCell.
import UIKit
protocol CollectionViewTableViewCellDelegate: AnyObject {
func collectionViewTableViewCellDidTapCell(_ cell: CollectionViewTableViewCell, viewModel: TitlePreviewViewModel)
}
class CollectionViewTableViewCell: UITableViewCell {
static let identifier = "CollectionViewTableViewCellId"
weak var delegate: CollectionViewTableViewCellDelegate?
private var titles: [Title] = [Title]()
private let collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.itemSize = CGSize(width: 140, height: 200)
layout.scrollDirection = .horizontal
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.register(TitleCollectionViewCell.self, forCellWithReuseIdentifier: TitleCollectionViewCell.identifier)
return collectionView
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.addSubview(collectionView)
collectionView.delegate = self
collectionView.dataSource = self
}
required init?(coder: NSCoder) {
fatalError()
}
override func layoutSubviews() {
super.layoutSubviews()
collectionView.frame = contentView.bounds
}
public func configure(with titles: [Title]) {
self.titles = titles
DispatchQueue.main.async { [weak self] in
self?.collectionView.reloadData()
}
}
I extend this class CollectionViewTableViewCell as follows:
extension CollectionViewTableViewCell: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: TitleCollectionViewCell.identifier, for: indexPath) as? TitleCollectionViewCell else {
return UICollectionViewCell()
}
//-- get the specific data from the pass-in Data
guard let model = titles[indexPath.row].poster_path else {
return UICollectionViewCell()
}
//- pass data to TitleCollectionViewCell
cell.configure(with: model)
return cell
}
In my Viewcontroller:
I have UItableView to retrieve the cell data as follows:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: CollectionViewTableViewCell.identifier, for: indexPath) as? CollectionViewTableViewCell else {
return UITableViewCell()
}
cell.delegate = self
Base on the Problem: for UItableView,there is no such dequeue method: dequeueReusableCellWithReuseIdentifier.
How to solve this problem?
Thanks. Please kindly help me.

How to set tableview style to have subtitle programmatically while having dequeueReusableCell?

I want my tableView to have subtitle as well as being able to dequeue properly. I have referred to this link but it does not work for my code. What should I do?
My code is currently like this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
//Calling tableview for a reusable cell here will always return a cell
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = developerArray[indexPath.row].developerName
cell.detailTextLabel?.text = developerArray[indexPath.row].developerHP
return cell
}
Swift 5
//Declare the variable cell Identifier
let reuseCellIdentifier = “cellIdentifier”;
//Implementation of cellForRowAt
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: reuseCellIdentifier)
if (!(cell != nil)) {
cell = UITableViewCell(style: .subtitle, reuseIdentifier: reuseCellIdentifier)
}
cell?.textLabel?.text = //Title text
cell?.detailTextLabel?.text = //Subtitle text
return cell!
}
You can create custom cell using nib by adding labels
For creating custom cell refer this link:
Custom UITableViewCell from nib in Swift
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:MyCustomCell = self.tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as! MyCustomCell
cell.title.text = yourTitleArray[indexPath.row]
cell.detailLbl.text = yourDetailArray[indexPath.row]
return cell
}
So first of all, did you create your table using storyboard or code?
Either way you need to make sure you set the datasource and delegate to self, provided the class they are in conforms to :
UITableViewDataSource
and
UITableViewDelegate
myTable?.delegate = self
myTable?.dataSource = self
Also make sure you register your cell
myTable?.register(myCell.self, forCellReuseIdentifier: "myCell")
And when declaring your cell, you need to force it as the type
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! myCell
Below is a working sample of tableview with a created cell. I hope this helps.
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
// Create the tableview object
var myTable:UITableView?
override func viewDidLoad() {
// Set the size and location of the tableview
myTable = UITableView(frame: CGRect(x: 0, y: 0, width: 300, height: 600))
// register your cell
myTable?.register(myCell.self, forCellReuseIdentifier: "myCell")
// set the background color of the table, note this wont make a difference unless the cell background is changed.
myTable?.backgroundColor = UIColor.clear
// set the datasource and delegate to self
myTable?.delegate = self
myTable?.dataSource = self
// This is jsut for style, wether there should be seperators or not, and if the user can select multiple lines
myTable?.separatorStyle = .none
myTable?.allowsMultipleSelection = true
// Add the table to your view
self.view.addSubview(myTable!)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// This is declaring how many rows you want in your table. I have 1 but you can do it according to the size of your array.
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// create the cell
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! myCell
// set the title text for this cell
cell.title.text = "HelloWorld"
// return the cell
return cell
}
}
and this is the class for the cell we referenced above.
class myCell: UITableViewCell {
var title = UILabel()
var detail = UILabel()
override func awakeFromNib() {
super.awakeFromNib()
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
title.font = UIFont.boldSystemFont(ofSize: 16)
title.textAlignment = .center
self.contentView.addSubview(title)
self.contentView.addSubview(detail)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func layoutSubviews() {
super.layoutSubviews()
title.frame = CGRect(x: self.contentView.frame.width / 2 - 200, y: 6, width: 400, height: 20)
detail.frame = CGRect(x: 150, y: 10, width: 280, height: 20)
}
}
Let me know if this helps. if you are doing it from story board let me know and I'll adjust.

UICollectionView in UITableView Freezing while Scrolling

I create 1 demo Project like Gaana Application and for that i have added UICollectionView inside multiple UITableViewCell and it works fine but when i scroll UITableView, UITableView not scrolling smoothly.
can you guys please help me to fix this. below is my code.
override func viewDidLoad() {
super.viewDidLoad()
self.tblVW.tableFooterView = UIView(frame: CGRect.zero)
self.tblVW.separatorColor = UIColor.clear
let trendingNib = UINib(nibName: "TrendingCell", bundle: nil)
self.tblVW.register(trendingNib, forCellReuseIdentifier: "TrendingCell")
let topChartNib = UINib(nibName: "TopChartCell", bundle: nil)
self.tblVW.register(topChartNib, forCellReuseIdentifier: "TopChartCell")
let madeForYouNib = UINib(nibName: "MadeForYouCell", bundle: nil)
self.tblVW.register(madeForYouNib, forCellReuseIdentifier: "MadeForYouCell")
let newReleaseNib = UINib(nibName: "NewReleaseCell", bundle: nil)
self.tblVW.register(newReleaseNib, forCellReuseIdentifier: "NewReleaseCell")
let featuredArtistNib = UINib(nibName: "FeaturedArtistCell", bundle: nil)
self.tblVW.register(featuredArtistNib, forCellReuseIdentifier: "FeaturedArtistCell")
let discoverNib = UINib(nibName: "DiscoverCell", bundle: nil)
self.tblVW.register(discoverNib, forCellReuseIdentifier: "DiscoverCell")
let editorsPickNib = UINib(nibName: "EditorsPickCell", bundle: nil)
self.tblVW.register(editorsPickNib, forCellReuseIdentifier: "EditorsPickCell")
let gaanaSpecialNib = UINib(nibName: "GaanaSpecialCell", bundle: nil)
self.tblVW.register(gaanaSpecialNib, forCellReuseIdentifier: "GaanaSpecialCell")
}
func numberOfSections(in tableView: UITableView) -> Int {
return 8
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let cell:TrendingCell = self.tblVW.dequeueReusableCell(withIdentifier: "TrendingCell", for: indexPath) as! TrendingCell
cell.selectionStyle = UITableViewCellSelectionStyle.none
cell.layoutSubviews()
return cell
}
else if indexPath.section == 1 {
let cell:TopChartCell = self.tblVW.dequeueReusableCell(withIdentifier: "TopChartCell", for: indexPath) as! TopChartCell
cell.selectionStyle = UITableViewCellSelectionStyle.none
cell.layoutSubviews()
return cell
}
else if indexPath.section == 2 {
let cell:MadeForYouCell = self.tblVW.dequeueReusableCell(withIdentifier: "MadeForYouCell", for: indexPath) as! MadeForYouCell
cell.selectionStyle = UITableViewCellSelectionStyle.none
cell.layoutSubviews()
return cell
}
else if indexPath.section == 3 {
let cell:NewReleaseCell = self.tblVW.dequeueReusableCell(withIdentifier: "NewReleaseCell", for: indexPath) as! NewReleaseCell
cell.selectionStyle = UITableViewCellSelectionStyle.none
cell.layoutSubviews()
return cell
}
else if indexPath.section == 4 {
let cell:FeaturedArtistCell = self.tblVW.dequeueReusableCell(withIdentifier: "FeaturedArtistCell", for: indexPath) as! FeaturedArtistCell
cell.selectionStyle = UITableViewCellSelectionStyle.none
cell.layoutSubviews()
return cell
}
else if indexPath.section == 5 {
let cell:DiscoverCell = self.tblVW.dequeueReusableCell(withIdentifier: "DiscoverCell", for: indexPath) as! DiscoverCell
cell.selectionStyle = UITableViewCellSelectionStyle.none
cell.layoutSubviews()
return cell
}
else if indexPath.section == 6 {
let cell:EditorsPickCell = self.tblVW.dequeueReusableCell(withIdentifier: "EditorsPickCell", for: indexPath) as! EditorsPickCell
cell.selectionStyle = UITableViewCellSelectionStyle.none
cell.layoutSubviews()
return cell
}
else {
let cell:GaanaSpecialCell = self.tblVW.dequeueReusableCell(withIdentifier: "GaanaSpecialCell", for: indexPath) as! GaanaSpecialCell
cell.selectionStyle = UITableViewCellSelectionStyle.none
cell.layoutSubviews()
return cell
}
}
Below code is once of UITableViewCell code.
class TrendingCell: UITableViewCell {
#IBOutlet weak var trendingCollectionVW: UICollectionView!
var arrData = [String]()
override func awakeFromNib() {
super.awakeFromNib()
trendingCollectionVW.dataSource = self
trendingCollectionVW.delegate = self
let trendingSongsNib = UINib(nibName: "TrendingSongCollectionCell", bundle: nil)
trendingCollectionVW.register(trendingSongsNib, forCellWithReuseIdentifier: "TrendingSongCollectionCell")
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
// override func layoutSubviews() {
// super.layoutSubviews()
//
// self.layer.shouldRasterize = true
// self.layer.rasterizationScale = UIScreen.main.scale
// }
}
//MARK: - UICollectionView Delegate & DataSource
extension TrendingCell: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "TrendingSongCollectionCell", for: indexPath) as! TrendingSongCollectionCell
cell.lblTItle.text = String.init(format: "Indexpath %d", indexPath.item)
cell.imgVW.image = UIImage.init(named: String.init(format: "trendingSong%d", indexPath.item+1))
//cell.layoutSubviews()
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: (UIScreen.main.bounds.size.width - 15) / 2.9, height: 175)
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("You selected cell #\(indexPath.item)!")
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsetsMake(0, 5, 5, 5)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 5
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 5
}
}
When i scroll any of UICollectionView inside UITableViewCell, Suddenly 15 to 20 MB memory increased.
Edit
Here is demo Project Link : https://www.dropbox.com/s/k1vpdqn9moli9nw/Gaana.zip?dl=0
Any help will be appreciated.
Thanks
Here are several things you are doing wrong.
Problems:
Creating the nib every time
let trendingSongsNib = UINib(nibName: "TrendingSongCollectionCell", bundle: nil)
Registerring the nib in the wrong place
cell.trendingCollectionVW.register(trendingSongsNib, forCellWithReuseIdentifier: "TrendingSongCollectionCell")
Even after registering you try to dequeue using different reusing identifier
let cell:TrendingCell = self.tblVW.dequeueReusableCell(withIdentifier: "TrendingCell", for: indexPath) as! TrendingCell
Even if you successfully dequeue a cell, you create the nib again and again in each call
let trendingSongsNib = UINib(nibName: "TrendingSongCollectionCell", bundle: nil)
As each time collection view get created, it loads the data
// whole architecture is responsible for this, no particular code.
Solution
You can register the cell's #viewDidLoad or in the interface builder
Do not dynamically load nib, dequeue will automatically do it.
you are creating nibs every time which is already reusable and calling layoutSubviews() in cellfor method will create this issue
you can do following changes to your code
let trendingSongsNib = UINib(nibName: "TrendingSongCollectionCell", bundle: nil)
cell.trendingCollectionVW.register(trendingSongsNib, forCellWithReuseIdentifier: "TrendingSongCollectionCell")
put this lines in your cell's awakeFromNib()
so it will register cell at the time of creating your tableviewcell
and stop calling cell.layoutSubviews()

Cells don't show up in a table view until minimal scrolling or tap (NOTE: nothing happens on a background thread)

I have a horizontally scrollable UICollectionView with three cells each of which are different subclasses of UICollectionViewCell. Each one of these cells contains a UITableView.
Inside of the first two cells, my table view cells are the same subclasses of UITableViewCell and have just a UIImageView. I use it to set its backgroundColor. Inside of the third cell, my table view's cells are different subclasses of UITableViewCell than in the previous two. They have both a UILabel and a UIImageView. The label has some dummy text, and I set imageView's backgroundColor to some color, again.
In order to follow MVC pattern, I use my UIViewController as a data source and a delegate for both collection view, and table view. Here is the code of UIViewController:
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
let collectionViewCellId = "collectionViewCell"
let tableViewCellId = "tableViewCell"
let collectionViewCellId2 = "collectionViewCellId2"
let collectionViewCellId3 = "collectionViewCellId3"
let tableViewCellDif = "tableViewCellDif"
var collectionViewIndex: Int?
#IBOutlet weak var collectionView: UICollectionView! {
didSet {
collectionView.delegate = self
collectionView.dataSource = self
collectionView.isPagingEnabled = true
}
}
//MARK: UITableViewDataSource
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let colors: [UIColor] = [.red, .green, .purple, .orange, .blue]
let colors2: [UIColor] = [.blue, .brown, .yellow, .magenta, .cyan]
if collectionViewIndex == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: tableViewCellId, for: indexPath) as! TableViewCell
cell.colorForImageView = colors[indexPath.row]
return cell
} else
if collectionViewIndex == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: tableViewCellId, for: indexPath) as! TableViewCell
cell.colorForImageView = colors2[indexPath.row]
return cell
} else
if collectionViewIndex == 2 {
let cell = tableView.dequeueReusableCell(withIdentifier: tableViewCellDif, for: indexPath) as! TableViewCellDifferent
cell.colorForImageView = colors2[indexPath.row]
return cell
} else {
return UITableViewCell()
}
}
}
//MARK: UICollectionViewDataSource
extension ViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 3
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let identifier: String
if indexPath.item == 0 {
identifier = collectionViewCellId
} else if indexPath.item == 1 {
identifier = collectionViewCellId2
} else if indexPath.item == 2 {
identifier = collectionViewCellId3
} else {
identifier = ""
}
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath)
return cell
}
}
//MARK: UICollectionViewDelegate
extension ViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
if indexPath.item == 0 {
let cell = cell as! CollectionViewCell
cell.tableView.dataSource = self
cell.tableView.delegate = self
collectionViewIndex = 0
}
if indexPath.item == 1 {
let cell = cell as! CollectionViewCell2
cell.tableView.dataSource = self
cell.tableView.delegate = self
collectionViewIndex = 1
}
if indexPath.item == 2 {
let cell = cell as! CollectionViewCell3
cell.tableView.dataSource = self
cell.tableView.delegate = self
collectionViewIndex = 2
print (collectionViewIndex)
}
}
}
//MARK: UICollectionViewDelegateFlowLayout
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let layout = collectionViewLayout as! UICollectionViewFlowLayout
layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 0
return CGSize(width: collectionView.frame.width, height: collectionView.frame.height)
}
}
As I stated in a title of the question, nothing happens on a background thread. I, basically, only set the backgroundColor of table view's cells.
The problem is that inside the collection view's third cell (and only inside of there), my table view dequeues its cells only after a minor scroll or tap happens. Here is how it looks like:
I can't figure out why this happens. Maybe, this happens because inside of the third cell of the collection view, my table view's cells are instances of different subclass than inside of the first two?
EDITED
I could solve the problem by reloading the table view before before showing the collection view's each cell but I'm not sure that this is the most efficient solution. Here is the code:
//MARK: UICollectionViewDelegate
extension ViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
if indexPath.item == 0 {
let cell = cell as! CollectionViewCell
cell.tableView.dataSource = self
cell.tableView.delegate = self
cell.tableView.reloadData()
collectionViewIndex = 0
}
if indexPath.item == 1 {
let cell = cell as! CollectionViewCell2
cell.tableView.dataSource = self
cell.tableView.delegate = self
cell.tableView.reloadData()
collectionViewIndex = 1
}
if indexPath.item == 2 {
let cell = cell as! CollectionViewCell3
cell.tableView.dataSource = self
cell.tableView.delegate = self
cell.tableView.reloadData()
collectionViewIndex = 2
}
}
}
If you know a better way, I would appreciate your help.
I gave this a try, and saw the same results. So, I moved your collection view cell "setup" code from willDisplay cell: to cellForItemAt and it fixed the problem.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.item == 0 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: collectionViewCellId, for: indexPath) as! CollectionViewCell
cell.tableView.dataSource = self
cell.tableView.delegate = self
collectionViewIndex = 0
return cell
}
if indexPath.item == 1 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: collectionViewCellId2, for: indexPath) as! CollectionViewCell2
cell.tableView.dataSource = self
cell.tableView.delegate = self
collectionViewIndex = 1
return cell
}
// if we get here, indexPath.item must equal 2
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: collectionViewCellId3, for: indexPath) as! CollectionViewCell3
cell.tableView.dataSource = self
cell.tableView.delegate = self
collectionViewIndex = 2
return cell
}
Now, since you're not showing your code for your tableview cells, it's possible there might be another issue, but this worked for me:
class TableViewCell: UITableViewCell {
#IBOutlet var theImageView: UIImageView!
var colorForImageView: UIColor = UIColor.gray {
didSet {
self.theImageView.backgroundColor = colorForImageView
}
}
}
You can try to dequeue CollectionViewCell or TableViewCell explicitly on the main thread
DispatchQueue.main.async {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath)
}
or
DispatchQueue.main.async {
let cell = tableView.dequeueReusableCell(withIdentifier: tableViewCellDif, for: indexPath) as! TableViewCellDifferent
cell.colorForImageView = colors2[indexPath.row]
}
It could help to wake up the main thread
But in general, it would be much easier if a data source for the table view was inside collection view cell

CollectionView with images inside a TableViewCell

I want a TableView with 5 CollectionViews one by one. The Collection Views must display images that scroll horizontally. I followed this tutorial: https://ashfurrow.com/blog/putting-a-uicollectionview-in-a-uitableviewcell-in-swift/
But I want images scrolling in my CollectionView. I don't know how to do that.
Here is my sample code:
import UIKit
class ViewController1: UIViewController, UITableViewDataSource, UITableViewDelegate, UICollectionViewDelegateFlowLayout {
#IBOutlet var tableView: UITableView!
var images = [UIImage(named:"banner2"),UIImage(named:"banner1"),UIImage(named:"banner3"),UIImage(named:"banner4"),UIImage(named:"banner5")]
var storedOffsets = [Int: CGFloat]()
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell2", for: indexPath) as! Cell2
cell.frame = CGRect(x: 0, y: 28, width: 375, height: 202)
cell.myCollection.reloadData()
return cell
}
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
guard let tableViewCell = cell as? Cell2 else { return }
tableViewCell.setCollectionViewDataSourceDelegate(self, forRow: indexPath.row)
tableViewCell.collectionViewOffset = storedOffsets[indexPath.row] ?? 0
}
func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
guard let tableViewCell = cell as? Cell2 else { return }
storedOffsets[indexPath.row] = tableViewCell.collectionViewOffset
}
}
extension ViewController1: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return images.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell1", for: indexPath) as! Cell1
cell.image.image = images[(indexPath as NSIndexPath).row]
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("Collection view at row \(collectionView.tag) selected index path \(indexPath)")
}
}
import UIKit
class Cell1: UICollectionViewCell {
#IBOutlet var image: UIImageView!
override init(frame: CGRect) {
super.init(frame: frame)
image.frame = CGRect(x: 0, y: 0, width: 175, height: 175) // Here I get exc_bad_instruction (code=exc_i386_invop subcode=0x0) warning
contentView.backgroundColor = UIColor.green
self.contentView.addSubview(image)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
import UIKit
class Cell2: UITableViewCell {
#IBOutlet var myCollection: UICollectionView!
override func awakeFromNib() {
myCollection.frame = CGRect(x: 0, y: 28, width: 375, height: 202)
}
}
extension Cell2 {
func setCollectionViewDataSourceDelegate<D: UICollectionViewDataSource & UICollectionViewDelegate>(_ dataSourceDelegate: D, forRow row: Int) {
self.myCollection.delegate = dataSourceDelegate
self.myCollection.dataSource = dataSourceDelegate
myCollection.tag = row
myCollection.setContentOffset(myCollection.contentOffset, animated:false) // Stops collection view if it was scrolling.
let layout = UICollectionViewFlowLayout.init()
layout.scrollDirection = .horizontal
layout.sectionInset = UIEdgeInsets(top: 5, left: 0, bottom: 5, right: 0)
layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 0
layout.itemSize = CGSize(width: 182, height: 182)
myCollection = UICollectionView.init(frame: myCollection.frame, collectionViewLayout: layout)
myCollection.register(Cell1.self, forCellWithReuseIdentifier: "cell1")
self.contentView.addSubview(myCollection)
myCollection.reloadData()
}
var collectionViewOffset: CGFloat {
set { myCollection.contentOffset.x = newValue }
get { return myCollection.contentOffset.x }
}
}
A pink TableView (I set the colour) and black CollectionView(Again, I set the colour) I can't view my images or my CollectionViewCells that I have set as greenColor. And when touch in the pink part or try scroll I get the error: fatal error: unexpectedly found nil while unwrapping an Optional value with the warning - exc_bad_instruction (code=exc_i386_invop subcode=0x0)
Can someone tell me how I can do this? Thanks in advance
I use Xcode 8 and Swift 3.
I have snippet in OBJECTIVE- C , i think you have to convert into swift 3.0
Here is my code
You can try it
tablecell cellForRowAtIndexPath
static NSString *CellIdentifier = #"cell3";
CollectionViewTableCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[CollectionViewTableCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
cell.headerLabel.text = #"DISH TYPE";
cell.collectionView.delegate = self;
cell.collectionView.dataSource = self;
cell.collectionView.tag = indexPath.row;
[cell.collectionView reloadData];
and collection view cellForItemAtIndexPath
if(collectionView.tag == 4){
FIPhotoCollectionCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"foodTagCell" forIndexPath:indexPath];
NSDictionary *dic = [arrAmenities objectAtIndex:indexPath.row];
NSMutableDictionary *dic1 = [selectedAmenities objectAtIndex:indexPath.row];
NSURL *url = [NSURL URLWithString:[dic valueForKey:#"ImageURL"]];
[cell.photoImage sd_setImageWithURL:url placeholderImage:[UIImage imageNamed:#""]];
return cell;
}

Resources