为何UICollectionViewDelegateFlowLayout方法在设备转回竖屏时未被调用?
解决UICollectionView旋转时竖屏布局不更新的问题
这个问题我之前也踩过坑!核心原因要么是代理没设置对,要么是旋转时没触发布局刷新,咱们一步步来搞定:
1. 先确认代理协议和委托设置正确
首先,你的类需要同时继承 UICollectionViewDelegate 和 UICollectionViewDelegateFlowLayout,而且必须把FlowLayout的代理指向自己,不然sizeForItemAt这类布局代理方法根本不会被调用:
class GridViewNw: UIView, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout { var gridCollectionView: UICollectionView! var imgArrCount: Int = 20 // 在初始化CollectionView的方法里,一定要设置FlowLayout的delegate func setupCollectionView() { let flowLayout = UICollectionViewFlowLayout() flowLayout.minimumInteritemSpacing = 8 // 自定义你的间距 flowLayout.minimumLineSpacing = 8 flowLayout.sectionInset = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8) // 关键!这里必须把flowLayout的delegate设为self flowLayout.delegate = self gridCollectionView = UICollectionView(frame: self.bounds, collectionViewLayout: flowLayout) gridCollectionView.delegate = self gridCollectionView.dataSource = self // 别忘了设置数据源 gridCollectionView.register(YourCellClass.self, forCellWithReuseIdentifier: "cellID") // 添加约束让CollectionView铺满父View(旋转时bounds才会正确更新) gridCollectionView.translatesAutoresizingMaskIntoConstraints = false addSubview(gridCollectionView) NSLayoutConstraint.activate([ gridCollectionView.topAnchor.constraint(equalTo: self.topAnchor), gridCollectionView.leadingAnchor.constraint(equalTo: self.leadingAnchor), gridCollectionView.trailingAnchor.constraint(equalTo: self.trailingAnchor), gridCollectionView.bottomAnchor.constraint(equalTo: self.bottomAnchor) ]) } }
2. 实现布局代理方法,根据横竖屏计算单元格大小
在sizeForItemAt里根据当前设备方向判断列数,计算正确的单元格尺寸:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { // 竖屏4列,横屏5列 let columnCount = UIDevice.current.orientation.isPortrait ? 4 : 5 let spacing: CGFloat = 8 // 和FlowLayout里设置的间距保持一致 // 计算总间距:左右内边距 + 列之间的间距 let totalSpacing = spacing * CGFloat(columnCount + 1) // 可用宽度 = CollectionView宽度 - 总间距 let availableWidth = collectionView.bounds.width - totalSpacing // 单元格宽度 = 可用宽度 / 列数 let itemWidth = availableWidth / CGFloat(columnCount) // 这里假设是正方形单元格,你可以自定义高度 return CGSize(width: itemWidth, height: itemWidth) }
3. 监听设备旋转,强制刷新布局
系统有时候不会自动触发布局代理方法,所以我们需要在旋转时手动让CollectionView刷新布局。推荐用traitCollectionDidChange(更符合AutoLayout的逻辑):
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { super.traitCollectionDidChange(previousTraitCollection) // 当横竖屏切换时,水平尺寸类会变化,此时刷新布局 if previousTraitCollection?.horizontalSizeClass != traitCollection.horizontalSizeClass { gridCollectionView.collectionViewLayout.invalidateLayout() } }
如果你习惯用通知监听,也可以这么做(记得在deinit里移除监听):
override func awakeFromNib() { super.awakeFromNib() setupCollectionView() // 添加旋转通知监听 NotificationCenter.default.addObserver(self, selector: #selector(handleRotation), name: UIDevice.orientationDidChangeNotification, object: nil) } @objc private func handleRotation() { // 强制刷新布局,触发代理方法重新计算单元格大小 gridCollectionView.collectionViewLayout.invalidateLayout() } deinit { NotificationCenter.default.removeObserver(self) }
最后检查几个常见坑
- 确保CollectionView的约束是铺满父View的,不然旋转时它的bounds不会正确更新,计算出来的尺寸会出错。
- 不要给FlowLayout设置
estimatedItemSize为固定值(除非你需要动态大小),否则可能会干扰布局刷新。 - 如果你的CollectionView是在代码里创建的,一定要在
setupCollectionView里正确设置FlowLayout的代理,而不是只设置CollectionView的delegate。
这样调整后,从横屏转回竖屏时,布局代理方法就会被正确调用,单元格大小也会自动调整啦!
内容的提问来源于stack exchange,提问作者Rama Mahapatra
相关产品推荐
相关产品推荐

