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

不想你离开。 提交于 2019-12-14 03:53:31

问题


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:

**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:

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


回答1:


the answer is using Tags I just needed to use tag for them and use if else to choose which collection view has selected with tag so the answer is this :

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

and I should use this in both cellForRowAtIndexPath and numberOfRows methods you can use this for table view too




回答2:


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. Select the attributes inspector tab. Under reuse identifier make sure you put in extera_infoCollectionViewCell




回答3:


If you take the other tableview cell In different class , with NSObject features of storyboard it can help you , And it is easy to maintain .




回答4:


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:

Each Collection View you use will need its own dequeueReusableCell identifier:

  1. In interface-builder, name all your identifiers for your collection view cells. CatPicCell & DogPicCell for instance.
  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). If using switch/case, your value can be set to collectionView.tag. Tags can be numbered to identify each different 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.
  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). 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". 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.
  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.

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

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



来源:https://stackoverflow.com/questions/47530933/why-two-collection-views-in-two-table-view-cells-wont-work-in-swift-4

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!