为什么两个 table 视图单元格中的两个集合视图在 Swift 4 中不起作用?

Why two collection views in two table view cells won't work in Swift 4?

我阅读了类似的问题,例如如何在多个 table 视图单元格中拥有多个集合视图,并且我连接了我的集合视图单元格并为它们使用了标识符名称,但我不知道为什么会收到此错误:

* Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'could not dequeue a view of kind: UICollectionElementKindCell with identifier extera_infoCollectionViewCell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard' * First throw call stack:

** 请记住,我读过类似的问题,第一个 table 带有集合视图的视图单元格运行良好,问题是第二个 ** 这是我的主视图控制器代码,它有一个 table 视图,table 视图有两个单元格

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

    if collectionView == fieldOfActivityCell().fieldofActivitiesCollectionView {
        let fullfields : String = self.adv.resultValue[0].work_field!
        let fullfieldsArr : [String] = fullfields.components(separatedBy: ",")
        print(fullfieldsArr)
        return fullfieldsArr.count

    } else {

        let extera_infofields : String = self.adv.resultValue[0].extera_info!
        let extera_infofieldsArr : [String] = extera_infofields.components(separatedBy: ",")
        print(extera_infofieldsArr)
        return extera_infofieldsArr.count
    }
}

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

    if collectionView == fieldOfActivityCell().fieldofActivitiesCollectionView {

        let fieldsCells = collectionView.dequeueReusableCell(withReuseIdentifier: "fieldOfActivityCollectionViewCell", for: indexPath) as! fieldOfActivityCollectionViewCell

        let fullfields : String = self.adv.resultValue[0].work_field!
        let fullfieldsArr : [String] = fullfields.components(separatedBy: ",")

        fieldsCells.title.text = fullfieldsArr[indexPath.row]

        return fieldsCells
    }
    else {
        let extera_infoCells = collectionView.dequeueReusableCell(withReuseIdentifier: "extera_infoCollectionViewCell", for: indexPath) as! extera_infoCollectionViewCell

        let extera_info : String = self.adv.resultValue[0].extera_info!
        let extera_infoArr : [String] = extera_info.components(separatedBy: ",")

        extera_infoCells.infoText.text = extera_infoArr[indexPath.row]

        return extera_infoCells
    }
}

这是同一视图控制器中的 table 视图代码:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    if indexPath.row == 0{

        let fieldCell = self.showAdvTableView.dequeueReusableCell(withIdentifier: "fieldOfActivityCell", for: indexPath) as! fieldOfActivityCell

        return fieldCell

    } else {

        let fieldCell = self.showAdvTableView.dequeueReusableCell(withIdentifier: "extera_infoCell", for: indexPath) as! extera_infoCell

        return fieldCell
   }

这里是 table 查看第一个单元格 class:

class fieldOfActivityCell: UITableViewCell {

    @IBOutlet weak var fieldofActivitiesCollectionView: UICollectionView!

    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code

        if let flowLayout = fieldofActivitiesCollectionView.collectionViewLayout as? UICollectionViewFlowLayout { flowLayout.estimatedItemSize = CGSize.init(width: 1.0, height: 1.0) }
    }

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

        // Configure the view for the selected state
    }
}

extension fieldOfActivityCell {

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

    {
        fieldofActivitiesCollectionView.delegate = dataSourceDelegate
        fieldofActivitiesCollectionView.dataSource = dataSourceDelegate
        fieldofActivitiesCollectionView.reloadData()
    }
}

这是第二个 table视图单元格 class:

@IBOutlet weak var extra_infoCollectionView: UICollectionView!

override func awakeFromNib() {
    super.awakeFromNib()

    if let flowLayout = extra_infoCollectionView.collectionViewLayout as? UICollectionViewFlowLayout { flowLayout.estimatedItemSize = CGSize.init(width: 1.0, height: 1.0) }
}
}

 extension extera_infoCell {

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

    {
        extra_infoCollectionView.delegate = dataSourceDelegate
        extra_infoCollectionView.dataSource = dataSourceDelegate
        extra_infoCollectionView.reloadData()
    }
}

根据您的错误,您的重用标识符与故事板中的任何单元都不匹配。单击界面生成器中的 extera_info collectionView 单元格。 Select 属性检查器选项卡。在 重用标识符 下确保输入 extera_infoCollectionViewCell

如果你把其他的tableview cell放在不同的class中,用storyboard的NSObject特性可以帮你,而且维护起来也很方便。

第一步: 使用标签 - 你只需要为它们使用标签,然后使用 if else 来选择哪个集合视图选择了标签,所以答案是这样的:

if collectionView.tag == 1 {
do some thing//////
}else {
do some thing else}

你应该在 cellForRowAtIndexPath 和 numberOfRows 方法中使用它你也可以将它用于 table 视图

第二步: 您必须更改要在 CollectionView 数据源的 cellForRowAt 方法中出列的 'collection view' 的名称:

if collectionView.tag == 1 {
    let cell = yourFirstCollectionView.dequeueReusableCell(...) as yourCell
    ....
    return cell
} else {
    let cell = yourSecondCollectionView.dequeueReusableCell(...) as yourCell
    ....
    return cell
}

上面 Saeed 的标签选项可能是最简单的答案,但发现他的描述有点短,所以在下面为那些以前从未使用过标签的人添加更完整的答案...

如果遵守 MVC 并将 collectionView dataSource 方法放在 UITableView class 中(而不是在 UITableViewCell classes 中),并希望避免此“错误:

您使用的每个集合视图都需要自己的 dequeueReusableCell 标识符:

  1. 在界面生成器中,为您的集合视图单元格命名所有标识符。例如 CatPicCell 和 DogPicCell。
  2. 在您的 CellForItemAt collectionView 方法中,设置 if 语句或 switch 语句,以便将每个重用标识符设置为等于您在界面构建器中创建的标识符(第 1 步)。如果使用 switch/case,您的值可以设置为 collectionView.tag。可以对标签进行编号以标识每个不同的 collectionView。标签就像将您的一组 collectionView 变成一个字典或数组,这样每个 collectionView 都有自己独特的 key/index.
  3. 返回界面生成器,进入情节提要和 select 每个集合视图(每个集合视图都应位于其自己的 tableView 单元格中)。在 Xcode 的 "attribute inspector" 中,向下滚动到 "View" 部分并向下滚动 3 个空格 (Xcode 11,Swift 5),您会看到一个名为 "Tag"。为该集合视图分配一个整数值,然后为将要嵌入到您的 UITableView 单元格中的每个集合视图重复此过程。
  4. 一旦你用唯一整数标记了所有集合视图,你只需将你的案例设置为整数,并为每个 dequeueReusableCell 标识符提供与你在故事板中提供的相同的整数索引。

现在,当您在 TableViewCell classes 中输出的 collectionView 上调用 tableView 单元格时,它将能够获取正确的 dequeueReusable ID。您可以将数据放入每个开关盒中。

瞧,您现在拥有一套 collectionView 数据源所需的方法,但可以为您的所有集合视图提供服务。更好的是,当有人扩展项目并添加另一个 collectionView 时,就像在情节提要中向开关和标识符添加另一个案例一样简单。

示例代码可能如下所示:

        // I need a switch statement which will set the correct (of the 3 collectionViews) dequeueReusable IDENTIFIER for the collectionView
    switch collectionView.tag {
        //if tableView is doing cell == 1, then "CatsCell"
        //if ...                cell == 3, then "DogsCell"
        //if ...                cell == 5, then "BirdsCell"
    case 1:
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CatsCell", for: indexPath) as! CatsCVCell
        // put your required data here
        return cell
    case 3:
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "DogCell", for: indexPath) as! DogsCVCell
        // example data
        let dogs = dogController.fetch()
        cell.name = dogs[indexPath.item].dogName
        if let image = UIImage(data: groups[indexPath.item].image!) {

            cell.image = image
        }
        return cell
    case 5:
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "BirdCell", for: indexPath) as! BirdCVCell
        // put data code here for birds collection view cells
        return cell
    default:
        return UICollectionViewCell()  // or write a fatalError()
    }

注意:对于默认的 switch 语句,您有两个选项... 1. 像上面一样,一个通用但空的单元格实例 2.抛出错误。该错误永远不应该抛出,因为您将遇到所有情况,但是如果其他人改进了您的代码并添加了另一个 collectionView 但忘记添加 switch 案例,则可能会发生错误——因此请让您的错误声明准确解释错误。