如何用Compositional Layout实现自适配Cell嵌套自适配Collection View?
嵌套UICollectionView自适应高度问题解决(基于Compositional Layout)
问题说明
需要实现一个外层列表式UICollectionView(单元格占满屏幕宽度,高度随内容自适应),每个单元格内嵌套一个横向流式布局的子UICollectionView,子CollectionView占满父单元格宽度,其高度决定父单元格的高度。当前实现中子CollectionView高度为0,导致父单元格无法适配高度。
现有代码
外层CollectionView布局代码
static func autoListLayout(rowHeight: CGFloat, padding: CGFloat) -> UICollectionViewLayout { let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .estimated(rowHeight)) let item = NSCollectionLayoutItem(layoutSize: itemSize) item.contentInsets = NSDirectionalEdgeInsets.zero let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1)) let group = NSCollectionLayoutGroup.vertical(layoutSize: groupSize, subitems: [item]) group.interItemSpacing = .fixed(padding) let section = NSCollectionLayoutSection(group: group) section.orthogonalScrollingBehavior = .none section.contentInsets = NSDirectionalEdgeInsets(top: padding, leading: 0, bottom: padding, trailing: 0) section.interGroupSpacing = padding let layout = UICollectionViewCompositionalLayout(section: section) return layout }
子CollectionView添加约束代码
addSubview(subCollectionView) subCollectionView.isScrollEnabled = false subCollectionView.translatesAutoresizingMaskIntoConstraints = false subCollectionView.setContentHuggingPriority(.required, for: .vertical) subCollectionView.setContentCompressionResistancePriority(.required, for: .vertical) NSLayoutConstraint.activate([ subCollectionView.leftAnchor.constraint(equalTo: leftAnchor), subCollectionView.rightAnchor.constraint(equalTo: rightAnchor), subCollectionView.topAnchor.constraint(equalTo: topAnchor), subCollectionView.bottomAnchor.constraint(equalTo: bottomAnchor), ])
子CollectionView布局代码
static func subLayout(rowHeight: CGFloat, padding: CGFloat) -> UICollectionViewLayout { let itemSize = NSCollectionLayoutSize(widthDimension: .estimated(30), heightDimension: .estimated(rowHeight)) let item = NSCollectionLayoutItem(layoutSize: itemSize) item.contentInsets = NSDirectionalEdgeInsets.zero let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1)) let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) group.interItemSpacing = .fixed(padding) let section = NSCollectionLayoutSection(group: group) section.orthogonalScrollingBehavior = .none let layout = UICollectionViewCompositionalLayout(section: section) return layout }
解决方案
问题核心是布局尺寸设置错误,导致系统无法正确计算子CollectionView和父单元格的高度。需对两处布局代码进行修改:
1. 修改外层CollectionView的Group尺寸
外层CollectionView的垂直Group不能使用.fractionalHeight(1)(该设置会让Group高度占满父容器,而非自适应内容),需改为.estimated(rowHeight),让Group高度根据Item内容动态计算:
static func autoListLayout(rowHeight: CGFloat, padding: CGFloat) -> UICollectionViewLayout { let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .estimated(rowHeight)) let item = NSCollectionLayoutItem(layoutSize: itemSize) item.contentInsets = NSDirectionalEdgeInsets.zero // 修改此处:将heightDimension改为.estimated(rowHeight) let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .estimated(rowHeight)) let group = NSCollectionLayoutGroup.vertical(layoutSize: groupSize, subitems: [item]) group.interItemSpacing = .fixed(padding) let section = NSCollectionLayoutSection(group: group) section.orthogonalScrollingBehavior = .none section.contentInsets = NSDirectionalEdgeInsets(top: padding, leading: 0, bottom: padding, trailing: 0) section.interGroupSpacing = padding let layout = UICollectionViewCompositionalLayout(section: section) return layout }
2. 修改子CollectionView的Group尺寸
子CollectionView的横向Group同样不能使用.fractionalHeight(1),需改为.estimated(rowHeight),让Group高度由Item高度决定,从而让子CollectionView能正确计算自身的intrinsicContentSize:
static func subLayout(rowHeight: CGFloat, padding: CGFloat) -> UICollectionViewLayout { let itemSize = NSCollectionLayoutSize(widthDimension: .estimated(30), heightDimension: .estimated(rowHeight)) let item = NSCollectionLayoutItem(layoutSize: itemSize) item.contentInsets = NSDirectionalEdgeInsets.zero // 修改此处:将heightDimension改为.estimated(rowHeight) let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .estimated(rowHeight)) let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) group.interItemSpacing = .fixed(padding) let section = NSCollectionLayoutSection(group: group) section.orthogonalScrollingBehavior = .none let layout = UICollectionViewCompositionalLayout(section: section) return layout }
额外注意事项
- 确保子CollectionView的
isScrollEnabled已设置为false(你当前代码已实现),只有禁止滚动时,UICollectionView才会根据内容计算intrinsicContentSize。 - 子CollectionView的数据更新后,若高度未自动刷新,可调用
subCollectionView.invalidateIntrinsicContentSize()或让父单元格重新布局。
内容的提问来源于stack exchange,提问作者stackunderflow
相关产品推荐
相关产品推荐

