UICollectionView中cellForItemAt触发index out of range数组越界问题
数组越界(index out of range)问题排查与解决
问题原因
UICollectionView数据源逻辑完全倒置
numberOfItemsInSection方法的作用是返回当前section下的item总数,但你现在返回的是cellTypes.count(也就是section的总数量)。这会导致每个section被分配了远大于实际子数组长度的item数,当indexPath.row超过cellTypes[indexPath.section].count时,直接触发越界错误。- 同时缺失
numberOfSections方法,UICollectionView默认section数量为1,和你cellTypes的多section结构不匹配,进一步加剧索引混乱。
单元格配置的索引匹配风险
在configureTorontoCell中直接用indexPath.row访问torPhotos,如果section和城市类型的对应关系出错,或者row索引与目标数组长度不匹配,也会触发越界。异步数据加载的时序问题
若photoViewModel.getPhotos()是异步操作,fillCellTypes执行时各城市照片数组可能还未被正确填充,子数组长度为0,后续数据源方法返回的item数与实际数据不匹配,引发索引错误。
解决办法
1. 修正UICollectionView数据源方法
补充并修正核心数据源方法,确保section和item的数量逻辑正确:
extension WeatherDetailVC: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { // 返回section总数,对应cellTypes的子数组数量 func numberOfSections(in collectionView: UICollectionView) -> Int { return cellTypes.count } // 返回当前section下的item数量,对应cellTypes子数组的长度 func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { guard section < cellTypes.count else { return 0 } return cellTypes[section].count } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard indexPath.section < cellTypes.count, indexPath.row < cellTypes[indexPath.section].count else { return UICollectionViewCell() // 返回空cell避免崩溃 } let cellType = cellTypes[indexPath.section][indexPath.row] switch cellType { case .tor: return configureCityCell(photos: photoViewModel.torPhotos, indexPath: indexPath) case .vanc: return configureCityCell(photos: photoViewModel.vancPhotos, indexPath: indexPath) case .calg: return configureCityCell(photos: photoViewModel.calgPhotos, indexPath: indexPath) case .ott: return configureCityCell(photos: photoViewModel.ottPhotos, indexPath: indexPath) case .mon: return configureCityCell(photos: photoViewModel.monPhotos, indexPath: indexPath) } } // 通用单元格配置方法,避免重复代码 private func configureCityCell(photos: [PhotoModel], indexPath: IndexPath) -> PhotoCollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: PhotoCollectionViewCell.identifier, for: indexPath) as! PhotoCollectionViewCell guard indexPath.row < photos.count else { return cell // 索引越界时返回空配置的cell } cell.setup(photos[indexPath.row]) return cell } }
2. 确保数据加载完成后再刷新UI
如果photoViewModel.getPhotos()是异步操作,需要在数据加载完成的回调中调用fillCellTypes():
func configCollectionView() { photoViewModel.getPhotos { [weak self] in guard let self = self else { return } self.photos = self.photoViewModel.photos self.vancPhotos = self.photoViewModel.vancPhotos self.calgPhotos = self.photoViewModel.calgPhotos self.ottPhotos = self.photoViewModel.ottPhotos self.torPhotos = self.photoViewModel.torPhotos self.monPhotos = self.photoViewModel.monPhotos self.fillCellTypes() // 数据加载完成后再填充并刷新 } }
(需确保getPhotos方法提供回调闭包,或通过Delegate/Combine等方式通知数据加载完成)
3. 优化cellTypes的构建逻辑(可选)
简化cellTypes填充逻辑,同时过滤空数组避免无效section:
func fillCellTypes() { cellTypes = [ Array(repeating: CellType.vanc, count: vancPhotos.count), Array(repeating: CellType.calg, count: calgPhotos.count), Array(repeating: CellType.mon, count: monPhotos.count), Array(repeating: CellType.ott, count: ottPhotos.count), Array(repeating: CellType.tor, count: torPhotos.count) ].filter { !$0.isEmpty } collectionView.reloadData() }
内容的提问来源于stack exchange,提问作者Mert Köksal
相关产品推荐
相关产品推荐

