具有多个部分的 UICollectionView
Posted
技术标签:
【中文标题】具有多个部分的 UICollectionView【英文标题】:UICollectionView with multiple sections 【发布时间】:2021-02-03 10:15:31 【问题描述】:大家好,我需要在 collectionView
中水平滚动显示 2 个部分。
第一部分需要 10 个单元格,第二部分需要 9 个单元格
我已经实现了这种方式,但我不明白为什么当我选择第 2 节的单元格时,indexPath.item
的值是第 1 节的单元格的值
编辑
你看到我的collectionView有2个部分设置单元格像这样..我不需要显示这样的单元格,但我需要第1部分在第一行,它必须显示像这样的项目同一行 (0,1 2 3 4 5) 和底行的第 2 部分,并显示顶行的项目
我哪里做错了?
private func commonInit() -> Void
backgroundColor = .clear
let cv = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
cv.backgroundColor = .clear
cv.delegate = self
cv.dataSource = self
cv.isPagingEnabled = true
cv.showsHorizontalScrollIndicator = false
addSubview(cv)
if let layout = cv.collectionViewLayout as? UICollectionViewFlowLayout
layout.scrollDirection = .horizontal
TimeSelectorCell.register(in: cv)
cv.anchor(top: topAnchor, leading: leadingAnchor, bottom: bottomAnchor, trailing: trailingAnchor)
func numberOfSections(in collectionView: UICollectionView) -> Int 2
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
if section == 0 return 10
else return 9
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: TimeSelectorCell.cellIdentifier, for: indexPath) as! TimeSelectorCell
return cell
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
print(indexPath.item)
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
.init(width: collectionView.frame.width / 4 , height: collectionView.frame.height / 2)
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat 0
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat 0
【问题讨论】:
在 'didSelectItemAt' 中,您可以使用 "indexPath.section" 来检查它是哪个部分。只是为了确认这是否真的是问题。 @rana5ohaib 我试过了,它总是返回 indexpath.section 0 直到单元格 10 .. 问题是第 0 部分似乎被分成两行,然后第 1 部分开始.. 而我需要部分0 位于顶部,第 1 部分位于第 0 部分的下方 你需要实现一个自定义的 UICollectionViewLayout。对于水平流布局,它将首先从上到下填充,然后向右移动。由于您有两行,如 sizeForItemAt 中指定的那样,第 0 部分将从上到下填充,然后从右到左填充,第 1 部分也是如此。 你能创建一个示例项目吗? @kAiN - 你想要第一个“部分”在“第一行”上包含单元格 0 到 9,第二部分在“第二行”上包含单元格 0 到 8?您希望各行彼此独立滚动吗? 【参考方案1】:在这些场景中,你必须使用的是 ios 13 在 CollectionView 中的 Compositional Layouts。这里有2个教程供你理解-
-
https://www.zealousweb.com/how-to-use-compositional-layout-in-collection-view/
https://medium.com/better-programming/ios-13-compositional-layouts-in-collectionview-90a574b410b8
WWDC 视频 - https://developer.apple.com/videos/play/wwdc2019/215/
对于您的情况,我已经调整了第二个教程中的示例
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
return (section == 0) ? 10 : 9
func numberOfSections(in collectionView: UICollectionView) -> Int
return 2
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
print(indexPath)
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: TimeSelectorCell.cellIdentifier, for: indexPath) as! TimeSelectorCell
return cell
private func commonInit()
let cv = UICollectionView(frame: view.bounds, collectionViewLayout: createLayoutDiffSection())
cv.autoresizingMask = [.flexibleWidth, .flexibleHeight]
TimeSelectorCell.register(in: cv)
cv.dataSource = self
cv.delegate = self
addSubview(cv)
到目前为止,它与您的代码几乎相同。现在 createLayoutDiffSection() 方法是 -
func createLayoutDiffSection() -> UICollectionViewLayout
let layout = UICollectionViewCompositionalLayout (sectionIndex: Int,
layoutEnvironment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection? in
let seperateFlow = true
let columns = (sectionIndex == 0) ? 10 : 9
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(1.0))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 2, bottom: 0, trailing: 2)
let groupHeight = NSCollectionLayoutDimension.fractionalWidth(0.2)
let widthFraction:CGFloat = (sectionIndex == 0) ? 1 : 0.9
let groupSizeWidth:CGFloat = seperateFlow ? (widthFraction * 2) : 1
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(groupSizeWidth),
heightDimension: groupHeight)
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: columns)
let section = NSCollectionLayoutSection(group: group)
if seperateFlow
section.orthogonalScrollingBehavior = .continuous
section.contentInsets = NSDirectionalEdgeInsets(top: 20, leading: 0, bottom: 20, trailing: 0)
return section
return layout
如果两个部分的滚动不是分开的,则设置 -
让seperateFlow = false
这里要理解的重点是-
大小类
我们可以根据需要使用四种尺寸选项,这样就无需进行计算即可获得所需的输出。让我们看看所有的尺寸等级:
-
fractionalWidth():使用fractionalWidth我们可以设置宽度/高度
单元格的宽度与其父级的宽度成正比
fractionalHeight():
使用fractionalHeight,我们可以设置单元格的宽度/高度
与其父母的身高成正比
absolute():使用绝对
值,我们可以将单元格的宽度/高度设置为固定值
estimate():使用估计值,我们可以设置单元格的宽/高
根据内容大小。系统将决定最优
内容的宽度/高度。
核心概念
要构建任何组合布局,需要实现以下四个类:
-
NSCollectionLayoutSize — 宽度和高度尺寸是
类型 NSCollectionLayoutDimension 可以通过设置
布局的小数宽度/高度(相对于其的百分比
容器),或通过设置绝对或估计大小。
NSCollectionLayoutItem — 这是您的布局单元格,在
屏幕大小。
NSCollectionLayoutGroup — 它以水平、垂直或自定义形式保存 NSCollectionLayoutItem。
NSCollectionLayoutSection - 这用于通过传递 NSCollectionLayoutGroup 来初始化部分。部分最终构成了组合布局。
【讨论】:
在此感谢您的建议。。虽然对我来说是第一次使用CompositionalLayout,但它非常完整,实际上我仍然无法理解它是如何工作的。无论如何,我尝试更改一些值,它似乎工作正常,所以谢谢。我只有一个问题.. 我注意到要使第 1 部分和第 2 部分的单元格尺寸都相同,两个部分的单元格数量必须相同,因为显然单元格的宽度是 1 的分数两个部分。 即使单元格的数量不同,我如何才能在两个部分中拥有相同的单元格大小?是否需要在包含 9 而不是 10 的部分中添加幽灵单元格? 我已经修改了代码。我添加了 widthFraction 常量。并修改了 groupSizeWidth 的代码。是的,组合布局一开始就很难掌握。请使用 WWDC 视频和我分享的第一个教程。这些都很好。 您好,再次感谢您的帮助.. 我已经实现了您的更改,但现在如果我需要滚动两个部分而不是单独滚动,设置 let separatorFlow = false 我的单元格会丢失所有尺寸和他们有如果我设置 let separatorFlow = true【参考方案2】: func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
if (indexPath.section == 0 )
// cell of section 1
print(indexPath.item)
else
// cell of section 2
print(indexPath.item)
你可以试试!
【讨论】:
以上是关于具有多个部分的 UICollectionView的主要内容,如果未能解决你的问题,请参考以下文章