为什么两个 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 标识符:
- 在界面生成器中,为您的集合视图单元格命名所有标识符。例如 CatPicCell 和 DogPicCell。
- 在您的 CellForItemAt collectionView 方法中,设置 if 语句或 switch 语句,以便将每个重用标识符设置为等于您在界面构建器中创建的标识符(第 1 步)。如果使用 switch/case,您的值可以设置为 collectionView.tag。可以对标签进行编号以标识每个不同的 collectionView。标签就像将您的一组 collectionView 变成一个字典或数组,这样每个 collectionView 都有自己独特的 key/index.
- 返回界面生成器,进入情节提要和 select 每个集合视图(每个集合视图都应位于其自己的 tableView 单元格中)。在 Xcode 的 "attribute inspector" 中,向下滚动到 "View" 部分并向下滚动 3 个空格 (Xcode 11,Swift 5),您会看到一个名为 "Tag"。为该集合视图分配一个整数值,然后为将要嵌入到您的 UITableView 单元格中的每个集合视图重复此过程。
- 一旦你用唯一整数标记了所有集合视图,你只需将你的案例设置为整数,并为每个 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 案例,则可能会发生错误——因此请让您的错误声明准确解释错误。
我阅读了类似的问题,例如如何在多个 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 标识符:
- 在界面生成器中,为您的集合视图单元格命名所有标识符。例如 CatPicCell 和 DogPicCell。
- 在您的 CellForItemAt collectionView 方法中,设置 if 语句或 switch 语句,以便将每个重用标识符设置为等于您在界面构建器中创建的标识符(第 1 步)。如果使用 switch/case,您的值可以设置为 collectionView.tag。可以对标签进行编号以标识每个不同的 collectionView。标签就像将您的一组 collectionView 变成一个字典或数组,这样每个 collectionView 都有自己独特的 key/index.
- 返回界面生成器,进入情节提要和 select 每个集合视图(每个集合视图都应位于其自己的 tableView 单元格中)。在 Xcode 的 "attribute inspector" 中,向下滚动到 "View" 部分并向下滚动 3 个空格 (Xcode 11,Swift 5),您会看到一个名为 "Tag"。为该集合视图分配一个整数值,然后为将要嵌入到您的 UITableView 单元格中的每个集合视图重复此过程。
- 一旦你用唯一整数标记了所有集合视图,你只需将你的案例设置为整数,并为每个 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 案例,则可能会发生错误——因此请让您的错误声明准确解释错误。