'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.



Solution 1:[1]

The problem may be caused because you are returning UICollectionViewCell() in

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

if the two conditions are not met.

If at any point the conditions are not met, you return a cell which is not retrieved by calling dequeueReusableCellWithReuseIdentifier:forIndexPath: as the exception says.

Try setting a breakpoint to confirm if this is what is actually happening.

Solution 2:[2]

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.

Solution 3:[3]

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

Solution 4:[4]

this happened to me too, with the following code:

func collectionView(_ collectionView: UICollectionView, cellForItemAt 
indexPath: IndexPath) -> UICollectionViewCell {
    
        guard let cell = 
collectionView.dequeueReusableCell(withReuseIdentifier: 
"selectedUserCell", for: indexPath) as? selectedUserCell  else { return 
UICollectionViewCell() }
    
        return cell
    
    
    }

The problem was that I forgot to assign the class selectedUserCell to the cell in storyboard/nib.. so once that was fixed, the correct cell was being returned.

Solution 5:[5]

Select collection view cell and add reuse identifier

enter image description here

Solution 6:[6]

I doubt the error come from this line return UICollectionViewCell(), could you use assert to check error condition ?

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

    // ....

    // I doubt error come from `return UICollectionViewCell()`
    // Could you insert `assert` to check error condition ?
    // assert(false,'Error condition for collectionView cell')
    return UICollectionViewCell()
}

Solution 7:[7]

Had this issue today. Truly the phrase "return cell" where cell is a UICollectionViewCell causes this issue.

My issue was because I was using guard let to unwrap an optional, and due to the fact that the function returns something, which in this case is a UICollectionViewCell, it expects that I return one. So I returned a UICollectionViewCell to shut out the error. Definitely, it came back to hunt me.

guard let model  = titles[indexPath.row].posterPath else {
   print("something went wrong")
   return TitleCollectionViewCell()}

So i was able to resolve by using nil coalescing which would not require that I return anything like in guard let method.

let model = titles[indexPath.row].posterPath ?? ""
return cell

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 John Snow
Solution 2 Moaz Khan
Solution 3 keshav
Solution 4 Akram Husseini
Solution 5 Mahendra
Solution 6 AechoLiu
Solution 7 Tesleem