TableViewCell中加载CollectionViewCell的XIB遇注册问题及替代方案咨询
嘿,首先得说,你想用TableView每行对应一个分类、嵌套CollectionView实现横向滚动的思路完全没问题!这是电商、内容类APP里超常见的实现方式,比如淘宝的分类商品列表、抖音的垂类内容推荐都是这么做的,所以这个方向很靠谱,你的问题主要出在XIB注册的时机和一些细节配置上。
先唠唠你现有代码的几个坑
CollectionView注册时机错得离谱
你现在把collectionView.register()放在cellForItemAt里,这个方法会被调用N次(每次创建Cell都跑一遍),不仅纯纯浪费性能,还可能导致注册冲突。注册Cell这种事儿,必须提前做,不能等到要创建Cell了才临时抱佛脚。TableViewCell里的CollectionView没绑定代理数据源
你在DiscoverViewController里写了CollectionView的delegate和dataSource,但你得确保TableViewCell里的那个CollectionView,它的delegate和dataSource已经指向了DiscoverViewController啊!不然它根本不知道该找谁要数据。CollectionView没配置横向滚动的布局
默认的CollectionView是纵向滚动的,你得给它设置UICollectionViewFlowLayout,把滚动方向改成横向,同时设置item大小、间距这些参数,不然就算有数据,也可能因为布局不对看不到内容。
给你改好的代码示例
第一步:修正DiscoverViewController的viewDidLoad
把TableView和CollectionView的注册都提前到这里来:
class DiscoverViewController: UIViewController { @IBOutlet weak var discoverTableView: UITableView! override func viewDidLoad() { super.viewDidLoad() // 注册TableViewCell(如果XIB已在Storyboard关联Identifier,这步可省略,但保险起见加上) discoverTableView.register(UINib(nibName: "CategoryTableViewCell", bundle: nil), forCellReuseIdentifier: "CategoryTableViewCell") // 设置TableView的代理和数据源 discoverTableView.delegate = self discoverTableView.dataSource = self } }
第二步:改造CategoryTableViewCell
把CollectionView的布局和注册放到Cell内部,代码更模块化:
class CategoryTableViewCell: UITableViewCell { @IBOutlet weak var collectionView: UICollectionView! override func awakeFromNib() { super.awakeFromNib() // 配置横向滚动的布局 let flowLayout = UICollectionViewFlowLayout() flowLayout.scrollDirection = .horizontal flowLayout.itemSize = CGSize(width: 120, height: 150) // 可根据UI需求调整大小 flowLayout.minimumLineSpacing = 12 // 横向item间距 flowLayout.sectionInset = UIEdgeInsets(top: 8, left: 16, bottom: 8, right: 16) // CollectionView内边距 collectionView.collectionViewLayout = flowLayout // 提前注册CollectionViewCell collectionView.register(UINib(nibName: "DiscoverCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "DiscoverCollectionViewCell") } // 给外界提供设置代理数据源的方法 func setupCollectionView(with delegate: UICollectionViewDelegate & UICollectionViewDataSource) { collectionView.delegate = delegate collectionView.dataSource = delegate } }
第三步:修正TableView和CollectionView的数据源方法
删掉之前错误的注册代码,同时给CollectionView绑定代理:
extension DiscoverViewController: UICollectionViewDelegate, UICollectionViewDataSource, UITableViewDelegate, UITableViewDataSource { // MARK: - CollectionView相关方法 func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return 5 // 实际项目里应为对应分类的内容数量 } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { // 无需重复注册,已提前完成 let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "DiscoverCollectionViewCell", for: indexPath) as! DiscoverCollectionViewCell cell.authorLabel.text = "分类内容 \(indexPath.item)" cell.coverImage.image = #imageLiteral(resourceName: "discover") return cell } // MARK: - TableView相关方法 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 5 // 分类的数量 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = discoverTableView.dequeueReusableCell(withIdentifier: "CategoryTableViewCell", for: indexPath) as! CategoryTableViewCell // 给CollectionView绑定代理数据源 cell.setupCollectionView(with: self) // 实际项目可在此传递当前分类的数据,比如cell.categoryData = categoryList[indexPath.row] return cell } }
关于替代方案的唠嗑
其实原生TableView嵌套CollectionView的方案已经足够稳定、灵活了,完全能满足你的需求。如果非要找替代方案的话:
- 用CollectionView嵌套CollectionView:外层CollectionView纵向滚动,每个Section对应一个分类,内层CollectionView横向滚动。这种方式和TableView嵌套逻辑类似,但可以利用CollectionView的Section特性,处理分组逻辑可能更顺手。
- 第三方库:市面上有一些专门处理双向滚动的库,但除非你有特别复杂的交互需求(比如联动滚动、复杂头部悬浮),不然原生方案完全够用,没必要额外引入第三方增加包体积。
按照上面的步骤改完,你的双向滚动应该就能正常显示啦!
内容的提问来源于stack exchange,提问作者dpat0074

