Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UICollectionView error: cells must be retrieved by calling -: -dequeueReusableCellWithReuseIdentifier:forIndexPath:

In my project I have multiple types of UITableview cell each containing UICollectionview.

Initially I loaded 2 tableViewcell in 2 sections of UITableview. At first it loads fine without any problem. If I scroll the first section it scrolls nice but when I try to scroll the second section it gives me the following error:

the cell returned from -collectionView:cellForItemAtIndexPath: does not have a reuseIdentifier - cells must be retrieved by calling -dequeueReusableCellWithReuseIdentifier:forIndexPath:

I have the following code to set up the process:

    class Home: UITableViewDelegate, UITableViewDataSource, UICollectionViewDelegate, UICollectionViewDataSource{

     override func viewDidLoad() {
        self.tableView.register(SliderViewCell.nib, forCellReuseIdentifier: SliderViewCell.identifier)
        self.tableView.register(GridViewCell.nib, forCellReuseIdentifier: GridViewCell.identifier)
        self.tableView.estimatedRowHeight = 80
        self.tableView.rowHeight = UITableViewAutomaticDimension

}
func numberOfSections(in tableView: UITableView) -> Int {
    return 2
}
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {

    let viewObj = self.viewObject[indexPath.section]
    if viewObj.itemType! == HomeViewItemType.Slider.rawValue{
    if let sliderCell = cell as? SliderViewCell {
        sliderCell.setCollectionViewDataSourceDelegate(self, forRow: indexPath.row)
    }
    }
    else if viewObj.itemType! == HomeViewItemType.Grid.rawValue{
    if let gridCell = cell as? GridViewCell {
        gridCell.setCollectionViewDataSourceDelegate(self, forRow: indexPath.row)
    }
    }


}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    if self.viewObject.count>0{
    let viewObj = self.viewObject[indexPath.section]
    if viewObj.itemType! == HomeViewItemType.Slider.rawValue{
        let cell:SliderViewCell = tableView.dequeueReusableCell(withIdentifier: SliderViewCell.identifier, for: indexPath) as! SliderViewCell
        cell.contentView.tag = indexPath.section
    return cell
}
else if viewObj.itemType! == HomeViewItemType.Grid.rawValue{
        let cell:GridViewCell = tableView.dequeueReusableCell(withIdentifier: GridViewCell.identifier, for: indexPath) as! GridViewCell
        cell.contentView.tag = indexPath.section
        return cell
}
    }
    return UITableViewCell()
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    print("selected")
}

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

        return self.viewObject[section].viewData.count

}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

    if viewObj.itemType! == HomeViewItemType.Slider.rawValue{
        if let cell:SliderCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: SliderCollectionViewCell.identifier, for: indexPath) as? SliderCollectionViewCell{

            if viewObj.viewData.count>0{
                if let imageURL = URL(string: viewData.imageLink!){
                    cell.icon.sd_setImage(with: imageURL, completed: nil)
                }
            }
            return cell
        }
    }
    else if viewObj.itemType! == HomeViewItemType.Grid.rawValue{
if let cell:GridCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: GridCollectionViewCell.identifier, for: indexPath) as? GridCollectionViewCell{

            if viewObj.viewData.count>0{
                if let imageURL = URL(string: viewData.icon!){
                    cell.icon.sd_setImage(with: imageURL, completed: nil)
                }

            }

            return cell
}
    }

    return UICollectionViewCell()
}

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    print("tapped")
}

    }

class SliderViewCell: UITableViewCell {

@IBOutlet weak var collectionView: UICollectionView!
static var nib:UINib {
    return UINib(nibName: identifier, bundle: nil)
}
static var identifier:String {
    return String(describing: SliderViewCell.self)
}
var collectionViewCellWidth : CGFloat = 15.0
var collectionViewCellHeight : CGFloat = 150.0
var cellSpaceWidth : CGFloat = 8.0

override func awakeFromNib() {
    super.awakeFromNib()
    self.configureCollectionViewCell(nibFile: SliderCollectionViewCell.nib, identifier: SliderCollectionViewCell.identifier, width: collectionView.frame.size.width, height: 80, topInset: 0, leftInset: 0, bottomInset: 0, rightInset: 0, cellSpace: cellSpaceWidth, interimSpace: 0.0, scrollInDirection: .horizontal)
}

override func setSelected(_ selected: Bool, animated: Bool) {
    super.setSelected(selected, animated: animated)

    // Configure the view for the selected state
}
}
class GridViewCell: UITableViewCell {

@IBOutlet weak var collectionView: UICollectionView!
static var nib:UINib {
    return UINib(nibName: identifier, bundle: nil)
}
static var identifier:String {
    return String(describing: GridViewCell.self)
}
var collectionViewCellWidth : CGFloat = 15.0
var collectionViewCellHeight : CGFloat = 150.0
var cellSpaceWidth : CGFloat = 8.0

override func awakeFromNib() {
    super.awakeFromNib()
    self.configureCollectionViewCell(nibFile: GridCollectionViewCell.nib, identifier: GridCollectionViewCell.identifier, width: collectionView.frame.size.width, height: 80, topInset: 0, leftInset: 0, bottomInset: 0, rightInset: 0, cellSpace: cellSpaceWidth, interimSpace: 0.0, scrollInDirection: .horizontal)
}

func configureCollectionViewCell(nibFile:UINib, identifier:String,width:CGFloat,height:CGFloat, topInset:CGFloat,leftInset:CGFloat,bottomInset:CGFloat,rightInset:CGFloat, cellSpace:CGFloat, interimSpace:CGFloat,scrollInDirection:UICollectionViewScrollDirection){

    let nib = nibFile
    collectionView.register(nib, forCellWithReuseIdentifier: identifier)
    let cellWidth : CGFloat = width//collectionView.frame.size.width
    let cellheight : CGFloat = height//collectionViewCellHeight
    let cellSize = CGSize(width: cellWidth , height:cellheight)
    let layout = UICollectionViewFlowLayout()
    layout.scrollDirection = scrollInDirection
    layout.itemSize = cellSize
    layout.sectionInset = UIEdgeInsets(top: topInset, left: leftInset, bottom: bottomInset, right: rightInset)
    layout.minimumLineSpacing = cellSpace
    layout.minimumInteritemSpacing = interimSpace
    collectionView.setCollectionViewLayout(layout, animated: true)
    collectionView.reloadData()

}
override func setSelected(_ selected: Bool, animated: Bool) {
    super.setSelected(selected, animated: animated)

    // Configure the view for the selected state
}
    }

    extension GridViewCell{

func setCollectionViewDataSourceDelegate<D: UICollectionViewDataSource & UICollectionViewDelegate>(_ dataSourceDelegate: D, forRow row:Int){

    collectionView.delegate = dataSourceDelegate
    collectionView.dataSource = dataSourceDelegate
    collectionView.reloadData()

}
}

I think the GridViewCell which loads in the 2nd section of UITableview causing the problem. But I've already added dequeueReusableCellWithReuseIdentifier in the collectionView:cellForItemAtIndexPath: method. So I'm clueless what's causing the error.

Any help would be greatly appreciated.

like image 380
Linkon Sid Avatar asked Mar 09 '18 07:03

Linkon Sid


People also ask

What do you mean by a value UICollectionView?

The collection view maintains a queue or list of view objects that the data source has marked for reuse. Instead of creating new views explicitly in your code, you always dequeue views. There are two methods for dequeueing views.

What is a UICollectionView in Swift?

From apple's documentation, UICollectionView is: An object that manages an ordered collection of data items and presents them using customizable layouts. The name and definition makes it clear, it is a way to display a Collection of UI Views in our app.


2 Answers

Here are two ways that you can use to handle your cells instead of if else then UICollectionViewCell()

let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath)

        if let gridCell = cell as? GridCollectionViewCell {
            // TODO: configure cell
        }

return cell

Another way is to

    guard let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath)
 as? GridCollectionViewCell else {

                //Handle error first so you can return a perfect cell if there is no error
  }

Remove UICollectionViewCell() it's causing the issue because there is no reuse identifier for that cell.

like image 110
Moaz Khan Avatar answered Oct 03 '22 11:10

Moaz Khan


i resolve this problem by i missed to return cell in one collection view. i am using multiple collection view and i missed to return cell in " let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CropName", for: indexPath) as! CropNameCollectionViewCell return cell

see somewhere you missed to return cell

like image 20
keshav Avatar answered Oct 03 '22 11:10

keshav