在UITableView中嵌入UICollectionView遇越界及滚动内容异常求助
问题原因分析
数组下标越界
- CustomCell中的
viewModel未做可选类型处理,初始化阶段若collectionView先于viewModel赋值调用数据源方法,会因访问空数组下标触发越界错误。 - 赋值
viewModel后未立即刷新collectionView,UITableView复用cell时,旧数据未被替换,导致数据源与展示内容不匹配,进而触发下标越界。
- CustomCell中的
滚动时单元格内容异常
- UITableView的复用机制导致CustomCell被重复利用,但UICollectionView未在每次赋值
viewModel后刷新,旧数据残留引发内容错乱。 sizeForItemAt中使用baseCollectionView.bounds.width计算item尺寸,cell未完成布局时该宽度可能为0或错误值,导致collectionView布局异常,滚动时内容显示混乱。
- UITableView的复用机制导致CustomCell被重复利用,但UICollectionView未在每次赋值
解决方案
1. 修复数组下标越界
- 修改CustomCell的
viewModel定义,添加可选类型并在赋值后刷新collectionView:class CustomCell: UITableViewCell { var viewModel: [Features]? { didSet { baseCollectionView.reloadData() } } // 其他原有代码... } - 安全访问collectionView数据源,处理
viewModel为nil的情况:extension CustomCell : UICollectionViewDelegate , UICollectionViewDataSource , UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return viewModel?.count ?? 0 } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CollectionViewCell.identifier, for: indexPath) as? CollectionViewCell, let item = viewModel?[indexPath.row] else { return UICollectionViewCell() } cell.titleView.text = item.title cell.overView.text = item.overview return cell } // 其他原有代码... }
2. 修复滚动内容异常
- 清空复用cell的旧数据,在
prepareForReuse中重置viewModel并刷新collectionView:class CustomCell: UITableViewCell { // 其他原有代码... override func prepareForReuse() { super.prepareForReuse() viewModel = nil baseCollectionView.reloadData() } } - 修正collectionView item尺寸计算逻辑,使用cell的宽度替代未布局完成的collectionView bounds:
或者在cell布局完成后更新collectionView布局:func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { let width = self.bounds.width / 2 let height = collectionView.bounds.height return CGSize(width: width, height: height) }override func layoutSubviews() { super.layoutSubviews() if let layout = baseCollectionView.collectionViewLayout as? UICollectionViewFlowLayout { layout.itemSize = CGSize(width: self.bounds.width / 2, height: baseCollectionView.bounds.height) baseCollectionView.collectionViewLayout.invalidateLayout() } }
3. 额外优化:增强UITableView数据源安全性
在ViewController的titleForHeaderInSection中添加越界判断:
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { guard section < featuresVM.count else { return nil } return featuresVM[section].imageName }
内容的提问来源于stack exchange,提问作者ibrahimtasdmr
相关产品推荐
相关产品推荐

