iOS Modern Collection View
TL;DR
- 使用的技术: Compositional layout + Diffable data source。iOS 14+。
- 创建 layout 以描述布局;
- 创建 dataSource 以提供数据和 view:
- 使用 CellRegistration 和 dequeueConfiguredReusableCell 来获取 cell;
- 使用 SupplementaryRegistration 和 dequeueConfiguredReusableSupplementary 来获取 header、footer 等 supplementary views。
Compositional Layout

如上图所示,一个 compositional layout 由若干个 section 组成,每个 section 又由若干个 group 组成,每个 group 又包含若干个 item,因此,创建一个 layout 的过程即先创建最小展示单元 item 开始,再创建 group,创建 section,创建 layout:
func createBasicListLayout() -> UICollectionViewLayout {
let itemSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(1.0)
)
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let groupSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .absolute(44)
)
let group = NSCollectionLayoutGroup.horizontal(
layoutSize: groupSize,
subitems: [item]
)
let section = NSCollectionLayoutSection(group: group)
let layout = UICollectionViewCompositionalLayout(section: section)
return layout
}
SectionProvider
如果需要多个布局不同的 sections,可以用下面的构造函数来创建 layout:
// UICollectionViewCompositionalLayout
init(
sectionProvider: @escaping UICollectionViewCompositionalLayoutSectionProvider,
configuration: UICollectionViewCompositionalLayoutConfiguration
)
其中闭包 sectionProvider 用于创建并返回 layout 的各个 secitons,声明如下:
typealias UICollectionViewCompositionalLayoutSectionProvider = (Int, NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection?
// arguments meaning: { sectionIndex, layoutEnvironment in ... }
该闭包也会在一些系统事件触发时被调用,如横竖屏切换、系统字体变更、iPad multitasking 引发的 size class 变更等,可以访问 layoutEnvironment 来获取相关信息,以适配这些场景。
LayoutConfiguration
UICollectionViewCompositionalLayoutConfiguration
scrollDirection: UICollectionView.ScrollDirection
- 滚动方向:
.vertical
|.horizontal
- 滚动方向:
interSectionSpacing: CGFloat
- Sections 之间的间距
contentInsetsReference: UIContentInsetsReference
- 定义 content inset 时的参考边界
.automatic
|.none
|.safeArea
|.layoutMargins
|.readableContent
-
- 红色为 safeArea,绿色为 layoutMargins,蓝色为 readableContent
-
boundarySupplementaryItems: [NSCollectionLayoutBoundarySupplementaryItem]
- 在整个 layout 边界处的一些附加 items,如全局 header、全局 footer、badge 等
LayoutEnvironment
包含 layout container 和 environment traits 的信息
container: NSCollectionLayoutContainer
contentSize: CGSize
effectiveContentSize: CGSize
:content insets 生效之后的 sizecontentInsets: NSDirectionalEdgeInsets
effectiveContentInsets: NSDirectionalEdgeInsets
traitCollection: UITraitCollection
List layout
Compositional layout 专门提供了一个接口来创建 list layout:
static func list(using: UICollectionLayoutListConfiguration) -> UICollectionViewCompositionalLayout
UICollectionLayoutListConfiguration 用于配置 list layout 的一些属性,构造函数中需要指定一个 appearance,如 .plain
,.sidebar
,.grouped
等。参见 Apple 文档 来查看可配置的属性,下面列出几种。
- headerMode
.none
:不展示 header;.supplementary
:使用 supplementary views 来展示 headers;.firstItemInSection
:使用 section 中的第一个 item 来作为 header。
Cell view 和 supplementary view 的获取
创建 dataSource 的时候,需要传入一个 cellProvider 来提供 cell view:
dataSource = UICollectionViewDiffableDataSource<Int, UUID>(collectionView: collectionView) {
(collectionView: UICollectionView, indexPath: IndexPath, itemIdentifier: UUID) -> UICollectionViewCell? in
// Configure and return cell.
}
之前我们需要调用 register
方法来注册所需要的 cell 类,然后在 cellProvider 中调用 dequeueReusableCell
方法来获取复用的 cell。iOS 14 提供了一组新的方法,通过 CellRegistration
来配置 cell,然后使用 dequeueConfiguredReusableCell
方法来获取复用的 cell,在调用该方法的时候会自动将 cell 注册到 collectionView 上。如:
let cellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, Int> { cell, indexPath, item in
var contentConfiguration = cell.defaultContentConfiguration()
contentConfiguration.text = "\(item)"
contentConfiguration.textProperties.color = .lightGray
cell.contentConfiguration = contentConfiguration
}
dataSource = UICollectionViewDiffableDataSource<Section, Int>(collectionView: collectionView) {
(collectionView: UICollectionView, indexPath: IndexPath, itemIdentifier: Int) -> UICollectionViewCell? in
return collectionView.dequeueConfiguredReusableCell(using: cellRegistration,
for: indexPath,
item: itemIdentifier)
}
注意不要在 cellProvider 里面创建 cell registration,会造成 cell 无法复用,且在 iOS 15+ 上直接报异常
对 cell 的配置通过 UIListContentConfiguration 来完成,调用 cell 的 defaultContentConfiguration 来获取一个包含一些预设属性的 configuration,根据传入的数据对其进行配置,然后再赋值回 cell 的 contentConfiguration。设置 contentConfiguration 会替换掉 cell 现有的 contentView。Cell configuration 具体可参见 Modern cell configuration (WWDC20 10027)。
Header、footer 等 supplementary views 的配置和 cell 类似,设置 dataSource 的 supplementaryViewProvider 属性来提供用于创建 view 的闭包,在闭包里面调用 dequeueConfiguredReusableSupplementary 来获取一个复用的 view,该方法接受一个闭包参数 SupplementaryRegistration 来配置 view。
References
posted on 2023-04-09 11:35 westwindrest 阅读(269) 评论(0) 编辑 收藏 举报
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 如何使用 Uni-app 实现视频聊天(源码,支持安卓、iOS)
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)