水平UICollectionView无数据时刷新父视图出现崩溃该如何解决?
崩溃根本原因
触发崩溃的核心是UICollectionView数据源更新与布局调整的时序不一致,结合你的代码逻辑具体原因如下:
- 你在API请求返回更新
shownStations、hiddenStations数据源后,没有先调用reloadData()同步collectionView的内部计数,就先修改了collectionView的frame。修改frame会触发collectionView的内部布局刷新,此时系统会尝试调整滚动位置适配新的frame,而collectionView内部还保留着旧数据的item计数、滚动位置信息,和最新的空数据源不匹配。 - 当数据源最终返回0个item时,系统内部布局逻辑尝试滚动到之前的有效item位置(比如之前的第0个item),但当前section下已经没有任何item,就触发了数组越界的内部一致性异常。
- 额外诱因:用设置零宽高frame的方式隐藏视图,会强制触发不必要的布局计算,相比直接用
isHidden属性隐藏更容易触发此类布局时序问题。
修复方案
按优先级提供以下修复方式,建议同时应用前两项:
- 调整数据源更新与刷新的时序
先调用reloadData()同步collectionView的内部状态,再修改frame或者隐藏视图,代码示例:
// 先刷新数据源状态 self.stationCollectionView?.reloadData() // 再调整布局/显隐 if self.shownStations.isEmpty { self.stationCollectionView?.isHidden = true // 不需要再设零frame,用isHidden性能更好也不会触发布局 } else { self.stationCollectionView?.isHidden = false self.stationCollectionView?.frame = CGRect(x: 0, y: 0, width: 414, height: 150) }
- 在numberOfItems数据源方法中加兜底判断,避免返回异常计数
当shownStations为空时,不管hiddenStations是否有值,都直接返回0,避免出现数据源为空但返回1个item的异常情况(防止cellForItem时也出现越界):
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { if self.shownStations.isEmpty { return 0 } if !self.hiddenStations.isEmpty { return shownStations.count + 1 } else { return shownStations.count } }
- 可选优化:如果需要保留零frame的设置,可在显隐时先清空collectionView的滚动偏移
修改frame前先把contentOffset置为零,避免系统尝试滚动到无效位置:
if self.shownStations.isEmpty { self.stationCollectionView?.contentOffset = .zero self.stationCollectionView?.frame = CGRect(x: 0, y: 0, width: 0, height: 0) } else { self.stationCollectionView?.frame = CGRect(x: 0, y: 0, width: 414, height: 150) }
内容的提问来源于stack exchange,提问作者David
相关产品推荐
相关产品推荐

