简体   繁体   English

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

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

I read similar questions such as how to have multiple collection view in multiple table view cells and I connected my collection views cells and use identifier names for them but I don't know why I receive this Error:我阅读了类似的问题,例如如何在多个表视图单元格中拥有多个集合视图,并且我连接了我的集合视图单元格并为它们使用了标识符名称,但我不知道为什么会收到此错误:

* 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: * 由于未捕获的异常“NSInternalInconsistencyException”而终止应用程序,原因:“无法将类型的视图出列:UICollectionElementKindCell 与标识符为 exera_infoCollectionViewCell - 必须为标识符注册一个笔尖或类或连接故事板中的原型单元” *首先抛出调用堆栈:

**Remember that I read Similar questions and the first table view cell with collection view working well and the problem is for second one ** here is my code for main view controller that has a table view and the table view has two cells **请记住,我阅读了类似的问题,第一个表视图单元格与集合视图运行良好,问题是第二个 ** 这是我的主视图控制器代码,它有一个表视图,表视图有两个单元格

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
    }
}

and here is the table view codes in same view controller:这是同一视图控制器中的表视图代码:

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
   }

here is table view first cell 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()
    }
}

and here is the second tableview cell class:这是第二个 tableview 单元格类:

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

在此处输入图片说明

First step: using Tags - you just need to use tag for them and use if else to choose which collection view has selected with tag so the answer is this :第一步:使用标签 - 你只需要为它们使用标签,并使用 if else 来选择哪个集合视图选择了标签,所以答案是这样的:

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

and you should use this in both cellForRowAtIndexPath and numberOfRows methods you can use this for table view too你应该在 cellForRowAtIndexPath 和 numberOfRows 方法中使用它你也可以将它用于表格视图

Second step: you have to change the name of 'collection view' that you are dequeueing inside the cellForRowAt method in CollectionView data source:第二步:您必须更改要在 CollectionView 数据源中的 cellForRowAt 方法中出列的“集合视图”的名称:

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

According to your error your reuse identifier doesn't match any cell in your storyboard.根据您的错误,您的重用标识符与故事板中的任何单元格都不匹配。 Click on your extera_info collectionView cell in interface builder.在界面构建器中单击您的 extra_info collectionView 单元格。 Select the attributes inspector tab.选择属性检查器选项卡。 Under reuse identifier make sure you put in extera_infoCollectionViewCell重用标识符下,确保你放入了exera_infoCollectionViewCell

如果你把其他的tableview cell 放在不同的类中,借助storyboard 的NSObject 特性,它可以帮助你,并且易于维护。

Saeed's tag option above is likely the simplest answer, but found his description a little short so adding a more complete answer below for those who've never used tags before...上面赛义德的标签选项可能是最简单的答案,但发现他的描述有点短,所以在下面为那些以前从未使用过标签的人添加一个更完整的答案......

If abiding by MVC and placing collectionView dataSource methods inside the UITableView class (instead of inside the UITableViewCell classes), and wanting to avoid this " error:如果遵守 MVC 并将 collectionView dataSource 方法放置在 UITableView 类中(而不是 UITableViewCell 类中),并且想要避免这个“错误:

Each Collection View you use will need its own dequeueReusableCell identifier:您使用的每个集合视图都需要自己的 dequeueReusableCell 标识符:

  1. In interface-builder, name all your identifiers for your collection view cells.在 interface-builder 中,为集合视图单元命名所有标识符。 CatPicCell & DogPicCell for instance.例如 CatPicCell 和 DogPicCell。
  2. In your CellForItemAt collectionView method, set up if-statements or switch statement such that each reuse identifier is set equal to the identifiers you created in interface-builder (step 1).在您的 CellForItemAt collectionView 方法中,设置 if 语句或 switch 语句,以便将每个重用标识符设置为等于您在 interface-builder 中创建的标识符(步骤 1)。 If using switch/case, your value can be set to collectionView.tag.如果使用 switch/case,您的值可以设置为 collectionView.tag。 Tags can be numbered to identify each different collectionView.可以对标签进行编号以标识每个不同的 collectionView。 The tags are like turning your set of collectionViews into a dictionary or array, such that each collectionView gets its own unique key/index.标签就像把你的 collectionViews 集变成一个字典或数组,这样每个 collectionView 都有自己唯一的键/索引。
  3. Go back into interface-builder, and go into your storyboard and select each collection view (each of which should be inside its own tableView cell).回到界面构建器,进入你的故事板并选择每个集合视图(每个集合视图都应该在它自己的 tableView 单元格内)。 In Xcode's "attribute inspector" scroll down to the "View" section and 3 spaces down (Xcode 11, Swift 5) you'll see a field called "Tag".在 Xcode 的“属性检查器”中向下滚动到“查看”部分,向下移动 3 个空格(Xcode 11、Swift 5),您将看到一个名为“标签”的字段。 Assign an integer value to that collection view, and then repeat this process for each collection view which is going to be embedded in your UITableView cells.为该集合视图分配一个整数值,然后对将要嵌入 UITableView 单元格中的每个集合视图重复此过程。
  4. Once you have all the collection views tagged with unique integers, you simply set your cases to the integers, and give each dequeueReusableCell identifier the same integer index as you provided in the storyboard.一旦你用唯一的整数标记了所有的集合视图,你只需将你的案例设置为整数,并为每个 dequeueReusableCell 标识符提供与故事板中提供的相同的整数索引。

Now when you tableView cell calls on the collectionView you've outletted in the TableViewCell classes, it will be able to acquire the proper dequeueReusable ID.现在,当您在 TableViewCell 类中输出的 collectionView 上调用 tableView 单元格时,它将能够获取正确的 dequeueReusable ID。 You can put your data inside each switch case.您可以将数据放入每个开关盒中。

Voila, you now have ONE collectionView datasource set of required methods, but serving ALL of your collection views.瞧,您现在拥有一组所需方法的 collectionView 数据源,但可以为您的所有集合视图提供服务。 EVEN BETTER, when someone expands the project and adds another collectionView it will be as easy as adding another case to the switch and identifier in the storyboard.更好的是,当有人扩展项目并添加另一个 collectionView 时,就像向故事板中的开关和标识符添加另一个案例一样简单。

Example code could look like this:示例代码可能如下所示:

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

note: you have two options for your default to the switch statement... 1. like above, a generic but empty cell instance 2. throw an error.注意:对于 switch 语句的默认设置,您有两个选项...... 1. 像上面一样,一个通用但空的单元格实例 2. 抛出一个错误。 The error should never throw bc you'll have all the cases, but an error could occur if someone else improves your code and add another collectionView but forgets to to add the switch case-- so make your error statement explain what's wrong precisely.错误永远不应该抛出 bc 您将拥有所有情况,但是如果其他人改进了您的代码并添加了另一个 collectionView 但忘记添加 switch 案例,则可能会发生错误 - 所以让您的错误声明准确地解释什么是错误的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM