UICollectionView 错误:必须通过调用来检索单元格 -: -dequeueReusableCellWithReuseIdentifier:forIndexPath:

Posted

技术标签:

【中文标题】UICollectionView 错误:必须通过调用来检索单元格 -: -dequeueReusableCellWithReuseIdentifier:forIndexPath:【英文标题】:UICollectionView error: cells must be retrieved by calling -: -dequeueReusableCellWithReuseIdentifier:forIndexPath: 【发布时间】:2018-03-09 07:56:05 【问题描述】:

在我的项目中,我有多种类型的UITableview 单元格,每个单元格都包含UICollectionview

最初我在UITableview 的两个部分中加载了 2 个tableViewcell。起初它加载正常,没有任何问题。如果我滚动第一部分,它滚动得很好,但是当我尝试滚动第二部分时,它给了我以下错误:

从 -collectionView:cellForItemAtIndexPath: 返回的单元格没有重用标识符 - 必须通过调用 -dequeueReusableCellWithReuseIdentifier:forIndexPath: 来检索单元格:

我有以下代码来设置流程:

    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()



我认为在 UITableview 的第二部分加载的 GridViewCell 会导致问题。但是我已经在collectionView:cellForItemAtIndexPath: 方法中添加了dequeueReusableCellWithReuseIdentifier。所以我不知道是什么导致了错误。

任何帮助将不胜感激。

【问题讨论】:

检查您的故事板单元格,您是否设置了标识符? 是的,我已经检查过了。它第一次加载没有任何问题。但是当我滚动时崩溃 你的 TableViewCell 是否包含 UICollectionView ?? 是的。有多种类型的tableview单元格,每个单元格都包含UICollectionView 我让你知道,在这种情况下,在 UIViewController 中同时保留 tableView 和 Collectionview 并不是一个好习惯。它搞砸了你的代码工作。我建议你像this那样划分你的结构。这里 tableview 与 uiviewcontroller 绑定,而 CollectionView 委托和数据源与 TableViewcell 绑定。通过这样做,您的代码结构会自动划分为适当的结构,以便您正确维护它。 【参考方案1】:

这个问题可能是因为你在返回 UICollectionViewCell()

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

如果这两个条件都不满足。

如果在任何时候不满足条件,则返回一个单元格,该单元格没有通过调用 dequeueReusableCellWithReuseIdentifier:forIndexPath: 检索到,如异常所述。

尝试设置断点以确认这是否是实际发生的情况。

【讨论】:

我通过在return UICollectionViewCell() 行中设置断点来调试它,它永远不会到达那一行。 我应该默认返回什么而不是 return UICollectionViewCell() 如果我省略这一行,编译器会给出错误 Missing return in a function expected to return UICollectionViewCell 虽然我已经返回细胞根据条件。有什么建议吗? 也许你没有正确调试唯一没有重用标识符的单元格是 UICollectionViewCell() .....尝试删除它而不是两个 if if else 写一个 if you总是在某个单元格而不是单元格对象上运行返回单元格命令。试试看。 @MoazKhan 这就是几分钟前所做的并解决了我的问题。非常感谢。 @John Snow,谢谢你把我带到正确的方向【参考方案2】:

这里有两种方法可以用来处理你的单元格,而不是 if else then UICollectionViewCell()

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

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

return cell

另一种方法是

    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
  

删除导致问题的 UICollectionViewCell(),因为该单元格没有重用标识符。

【讨论】:

【参考方案3】:

这也发生在我身上,代码如下:

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

问题是我忘记将 selectedUserCell 类分配给故事板/笔尖中的单元格。所以一旦修复,就会返回正确的单元格。

【讨论】:

【参考方案4】:

我解决了这个问题 我错过了在一个集合视图中返回单元格。我正在使用多个集合视图,我错过了返回单元格 " 让 cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CropName", for: indexPath) as! CropNameCollectionViewCell 返回单元格

看到你错过的地方返回单元格

【讨论】:

啊啊啊一小时发现我真的错过了return cell!!谢谢!【参考方案5】:

选择集合视图单元格并添加重用标识符

【讨论】:

没有。我已经添加了这个。它第一次加载没有任何问题。但是当我滚动时崩溃。【参考方案6】:

我怀疑错误来自return UICollectionViewCell()这一行,你能用assert检查错误情况吗?

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()

【讨论】:

以上是关于UICollectionView 错误:必须通过调用来检索单元格 -: -dequeueReusableCellWithReuseIdentifier:forIndexPath:的主要内容,如果未能解决你的问题,请参考以下文章

必须调用超类“UICollectionView”的指定初始化程序

UICollectionView 必须使用非零布局参数初始化'

UICollectionView 必须使用非零布局参数初始化

仅通过按下按钮滚动 UICollectionView

UICollectionView 和解析图像(Swift)没有错误但崩溃

使用自定义布局重新加载 UICollectionView 会引发错误