如何实现UICollectionView竖屏2个横屏5个的自适应布局?
解决UICollectionView横竖屏适配布局问题
核心问题修正
- 禁止重复创建CollectionView:原代码在旋转时会重建collectionView,既浪费资源又会丢失视图状态,只需在初始化时创建一次,旋转时仅更新布局参数。
- 用容器可用宽度替代屏幕尺寸:屏幕尺寸包含系统状态栏、导航栏等占用空间,正确的可用宽度应为collectionView父视图的
bounds.width减去左右安全区内边距。 - 通过代理动态计算ItemSize:相比手动设置
itemSize,实现UICollectionViewDelegateFlowLayout的sizeForItemAt方法,能精准适配容器尺寸变化与安全区。
具体代码修改
1. 初始化CollectionView(仅执行一次)
在viewDidLoad中完成collectionView的初始化,设置自动适配父视图尺寸:
override func viewDidLoad() { super.viewDidLoad() setupCollectionView() } private func setupCollectionView() { let layout = UICollectionViewFlowLayout() layout.minimumInteritemSpacing = 0 layout.minimumLineSpacing = 0 layout.scrollDirection = .vertical collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout) collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight] collectionView.dataSource = self collectionView.delegate = self collectionView.register(CharacterCell.self, forCellWithReuseIdentifier: CharacterCell.reuseIdentifier) view.addSubview(collectionView) }
2. 实现代理方法计算ItemSize
通过代理方法动态计算单元格尺寸,自动处理安全区:
extension YourViewController: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { // 计算可用宽度:减去左右安全区内边距 let availableWidth = collectionView.bounds.width - collectionView.safeAreaInsets.left - collectionView.safeAreaInsets.right let itemsPerRow: CGFloat = UIDevice.current.orientation.isPortrait ? 2 : 5 let itemWidth = availableWidth / itemsPerRow // 保持正方形单元格 return CGSize(width: itemWidth, height: itemWidth) } }
3. 处理设备旋转刷新布局
在旋转时通知collectionView刷新布局,无需重建视图:
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { super.viewWillTransition(to: size, with: coordinator) coordinator.animate(alongsideTransition: { _ in self.collectionView.collectionViewLayout.invalidateLayout() }) }
额外优化:脱离设备方向依赖
若不想依赖UIDevice.current.orientation(避免设备方向与界面方向不一致的情况),可通过容器宽高比判断横竖屏:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { let availableWidth = collectionView.bounds.width - collectionView.safeAreaInsets.left - collectionView.safeAreaInsets.right // 根据容器宽高比判断横竖屏 let itemsPerRow: CGFloat = collectionView.bounds.width < collectionView.bounds.height ? 2 : 5 let itemWidth = availableWidth / itemsPerRow return CGSize(width: itemWidth, height: itemWidth) }
内容的提问来源于stack exchange,提问作者Constantine
相关产品推荐
相关产品推荐

