Reputation: 531
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.
Upvotes: 19
Views: 19388
Reputation: 187
I have also faced this issue. make sure your cellForItemAt method should not return UICollectionViewCell() it should return the cell which is dequeued.
Check if you have added conditions which are failing and your default return is getting called and returning UICollectionViewCell() then this crash will come.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
return UICollectionViewCell()
}
Upvotes: 0
Reputation: 178
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
Upvotes: 2
Reputation: 95
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
Upvotes: 0
Reputation: 29
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.
Upvotes: 2
Reputation: 1352
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.
Upvotes: 4
Reputation: 18368
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()
}
Upvotes: 0
Reputation: 462
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.
Upvotes: 24