UICollectionView在iPhone11上单元格中间间距过大的优化方案咨询
根因分析
当前写法的计算逻辑存在缺陷,导致在iPhone 11(屏幕宽度414pt)这一临界尺寸下,单元格总宽度加边距的和超过了UICollectionView可用宽度,FlowLayout自动将第三个单元格挤到下一行,就出现了2列加中间空白的异常显示。
具体缺陷包括:
- 计算单元格宽度时,错误使用了父view的宽度而非collectionView本身的宽度,当collectionView和父view宽度不一致时就会出现计算偏差
- 没有对单元格宽度的计算结果做向下取整处理,浮点数精度误差会导致总宽度超出可用范围
- 计算边距总和时逻辑不严谨,没有明确区分section边距和单元格之间的间距
解决方案
方案1:修正FlowLayout尺寸计算逻辑(兼容所有iOS版本,推荐)
将间距抽为统一常量,修正尺寸计算逻辑即可,无需调整原本16的间距设置:
// 抽成统一常量,避免两处硬编码不一致 private let minimumInteritemSpacing: CGFloat = 16.0 let sectionInsets = UIEdgeInsets(top: 16.0, left: 16.0, bottom: 16.0, right: 16.0) extension MyController: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { return 16 } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { return minimumInteritemSpacing } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { let itemsPerRow: CGFloat = 3.0 // 总边距 = section左右边距 + 行内两个单元格的间距总和 let totalSpacing = sectionInsets.left + sectionInsets.right + minimumInteritemSpacing * (itemsPerRow - 1) // 用collectionView的实际宽度计算,更准确 let availableWidth = collectionView.bounds.width - totalSpacing // 向下取整避免浮点数精度导致总宽度超出 let widthPerItem = floor(availableWidth / itemsPerRow) return CGSize(width: widthPerItem, height: 59) } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { return self.sectionInsets } }
方案2:使用UICollectionViewCompositionalLayout(最低支持iOS13+,无需手动计算)
直接用系统提供的声明式布局,自动适配所有设备尺寸,不需要手动计算单元格宽度:
private func configureCollectionView(){ let layout = UICollectionViewCompositionalLayout { sectionIndex, layoutEnvironment in // 单个item尺寸:宽度为行宽的1/3,高度固定59 let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1/3), heightDimension: .absolute(59)) let item = NSCollectionLayoutItem(layoutSize: itemSize) // 单元格左右间距各8,总间距16,和原有设计一致 item.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 8, bottom: 0, trailing: 8) // 行尺寸:宽度和collectionView一致,高度固定59 let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(59)) let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) // section边距和原有设计一致 let section = NSCollectionLayoutSection(group: group) section.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 16, bottom: 16, trailing: 16) // 行间距和原有设计一致 section.interGroupSpacing = 16 return section } self.collectionView.collectionViewLayout = layout self.collectionView.register(UINib(nibName: "AmountCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "cell") self.collectionView.delegate = self self.collectionView.dataSource = self }
使用该方案后可以删除原有的UICollectionViewDelegateFlowLayout相关代理方法,系统会自动完成布局适配。
内容的提问来源于stack exchange,提问作者biggreentree
相关产品推荐
相关产品推荐

