使用Compositional Layout实现内嵌3个UICollectionView的动态UICollectionViewCell
动态嵌套UICollectionView布局问题
我尝试使用UICollectionView Compositional Layout实现动态高度单元格,目前的视图层级如下:
- UICollectionView (Compositional Layout)
- UICollectionViewCell
- innerStackView (垂直UIStack) // 包含帖子描述和「来自利雅得」标签
- ...
- UICollectionView (Compositional Layout)
- UICollectionView (Flow Layout - 横向滚动)
- UICollectionView(Compositional Layout)
- UICollectionViewCell
已尝试的实现方案
- 将内部集合视图添加到垂直堆栈视图中
- 将内部集合视图添加到容器视图中通过约束布局
现有代码
外层UICollectionView(Compositional Layout)布局代码
func setupCollectionViewLayout() -> UICollectionViewLayout { let size = NSCollectionLayoutSize( widthDimension: NSCollectionLayoutDimension.fractionalWidth(1), heightDimension: NSCollectionLayoutDimension.estimated(300) ) let item = NSCollectionLayoutItem(layoutSize: size) let group = NSCollectionLayoutGroup.vertical(layoutSize: size, subitem: item, count: 1) let section = NSCollectionLayoutSection(group: group) section.contentInsets = .zero section.interGroupSpacing = 0 let headerFooterSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalHeight(0.3) ) let sectionHeader = NSCollectionLayoutBoundarySupplementaryItem( layoutSize: headerFooterSize, elementKind: UICollectionView.elementKindSectionHeader, alignment: .top ) section.boundarySupplementaryItems = [sectionHeader] return UICollectionViewCompositionalLayout(section: section) }
方案1:基于容器视图约束实现的Cell代码
class FeedsCell: UICollectionViewCell { @IBOutlet weak var containerView: UIView! @IBOutlet weak var innerStackView: UIStackView! @IBOutlet weak var postDescription: UILabel! var imageGalleryCollectionView: UICollectionView! var reactionCollectionView: UICollectionView! var commentCollectionView: UICollectionView! override func awakeFromNib() { super.awakeFromNib() } func with(viewModel: FeedsCellViewModel) -> Self { postDescription.text = ".random(in: 0...255)" if !viewModel.imageProvider.dataSource.isEmpty { let imageLayout = setupImageLayout() imageGalleryCollectionView = UICollectionView(frame: .zero, collectionViewLayout: imageLayout) imageGalleryCollectionView.delegate = viewModel.imageProvider imageGalleryCollectionView.dataSource = viewModel.imageProvider containerView.addSubview(imageGalleryCollectionView) } if !viewModel.reactionProvider.dataSource.isEmpty { let reactionLayout = UICollectionViewFlowLayout() reactionLayout.scrollDirection = .horizontal reactionCollectionView = UICollectionView(frame: .zero, collectionViewLayout: reactionLayout) reactionCollectionView.showsHorizontalScrollIndicator = false reactionCollectionView.dataSource = viewModel.reactionProvider reactionCollectionView.delegate = viewModel.reactionProvider innerStackView.addArrangedSubview(reactionCollectionView) reactionCollectionView.anchor(heightConstant: 32) } if !viewModel.commentProvider.dataSource.isEmpty { let commentLayout = setupCommentLayout() commentCollectionView = UICollectionView(frame: .zero, collectionViewLayout: commentLayout) commentCollectionView.dataSource = viewModel.commentProvider commentCollectionView.delegate = viewModel.commentProvider containerView.addSubview(commentCollectionView) imageGalleryCollectionView.anchor(top: innerStackView.bottomAnchor, left: innerStackView.leftAnchor, bottom: commentCollectionView.topAnchor, right: innerStackView.rightAnchor) commentCollectionView.anchor(top: imageGalleryCollectionView.bottomAnchor, left: innerStackView.leftAnchor, bottom: containerView.bottomAnchor, right: innerStackView.rightAnchor) } return self } override func prepareForReuse() { super.prepareForReuse() imageGalleryCollectionView?.removeFromSuperview() reactionCollectionView?.removeFromSuperview() commentCollectionView?.removeFromSuperview() } func setupCommentLayout() -> UICollectionViewCompositionalLayout { let layout = UICollectionViewCompositionalLayout.list(using: .init(appearance: .insetGrouped)) return layout } func setupImageLayout() -> UICollectionViewCompositionalLayout { let mainItem = NSCollectionLayoutItem( layoutSize: NSCollectionLayoutSize( widthDimension: .fractionalWidth(2/3), heightDimension: .fractionalHeight(1.0))) mainItem.contentInsets = NSDirectionalEdgeInsets( top: 2, leading: 0, bottom: 0, trailing: 2) let pairItem = NSCollectionLayoutItem( layoutSize: NSCollectionLayoutSize( widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalHeight(0.5))) pairItem.contentInsets = NSDirectionalEdgeInsets( top: 2, leading: 2, bottom: 0, trailing: 2) let trailingGroup = NSCollectionLayoutGroup.vertical( layoutSize: NSCollectionLayoutSize( widthDimension: .fractionalWidth(1/3), heightDimension: .fractionalHeight(1.0)), subitem: pairItem, count: 2) let mainWithPairGroup = NSCollectionLayoutGroup.horizontal( layoutSize: NSCollectionLayoutSize( widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalHeight(4/9)), subitems: [mainItem, trailingGroup]) let nestedGroup = NSCollectionLayoutGroup.vertical( layoutSize: NSCollectionLayoutSize( widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalWidth(1.0)), subitems: [ mainWithPairGroup, ] ) let section = NSCollectionLayoutSection(group: nestedGroup) return UICollectionViewCompositionalLayout(section: section) } }
方案2:基于UIStackView实现的Cell代码
func with(viewModel: FeedsCellViewModel) -> Self { postDescription.text = ".random(in: 0...255)" if !viewModel.imageProvider.dataSource.isEmpty { let imageLayout = setupImageLayout() imageGalleryCollectionView = UICollectionView(frame: .zero, collectionViewLayout: imageLayout) imageGalleryCollectionView.delegate = viewModel.imageProvider imageGalleryCollectionView.dataSource = viewModel.imageProvider innerStackView.addArrangedSubview(imageGalleryCollectionView) } if !viewModel.reactionProvider.dataSource.isEmpty { let reactionLayout = UICollectionViewFlowLayout() reactionLayout.scrollDirection = .horizontal reactionCollectionView = UICollectionView(frame: .zero, collectionViewLayout: reactionLayout) reactionCollectionView.showsHorizontalScrollIndicator = false reactionCollectionView.dataSource = viewModel.reactionProvider reactionCollectionView.delegate = viewModel.reactionProvider innerStackView.addArrangedSubview(reactionCollectionView) reactionCollectionView.anchor(heightConstant: 32) } if !viewModel.commentProvider.dataSource.isEmpty { let commentLayout = setupCommentLayout() commentCollectionView = UICollectionView(frame: .zero, collectionViewLayout: commentLayout) commentCollectionView.dataSource = viewModel.commentProvider commentCollectionView.delegate = viewModel.commentProvider innerStackView.addArrangedSubview(commentCollectionView) } return self }
问题诉求
两种方案均出现布局异常,经过5天调试仍未解决,需要正确实现该动态嵌套集合视图的布局方案。
内容的提问来源于stack exchange,提问作者Faisal
相关产品推荐
相关产品推荐

