版本记录
版本号 | 时间 |
---|---|
V1.0 | 2021.01.25 星期一 |
前言
iOS中有关视图控件用户能看到的都在UIKit框架里面,用户交互也是通过UIKit进行的。感兴趣的参考上面几篇文章。
1. UIKit框架(一) —— UIKit动力学和移动效果(一)
2. UIKit框架(二) —— UIKit动力学和移动效果(二)
3. UIKit框架(三) —— UICollectionViewCell的扩张效果的实现(一)
4. UIKit框架(四) —— UICollectionViewCell的扩张效果的实现(二)
5. UIKit框架(五) —— 自定义控件:可重复使用的滑块(一)
6. UIKit框架(六) —— 自定义控件:可重复使用的滑块(二)
7. UIKit框架(七) —— 动态尺寸UITableViewCell的实现(一)
8. UIKit框架(八) —— 动态尺寸UITableViewCell的实现(二)
9. UIKit框架(九) —— UICollectionView的数据异步预加载(一)
10. UIKit框架(十) —— UICollectionView的数据异步预加载(二)
11. UIKit框架(十一) —— UICollectionView的重用、选择和重排序(一)
12. UIKit框架(十二) —— UICollectionView的重用、选择和重排序(二)
13. UIKit框架(十三) —— 如何创建自己的侧滑式面板导航(一)
14. UIKit框架(十四) —— 如何创建自己的侧滑式面板导航(二)
15. UIKit框架(十五) —— 基于自定义UICollectionViewLayout布局的简单示例(一)
16. UIKit框架(十六) —— 基于自定义UICollectionViewLayout布局的简单示例(二)
17. UIKit框架(十七) —— 基于自定义UICollectionViewLayout布局的简单示例(三)
18. UIKit框架(十八) —— 基于CALayer属性的一种3D边栏动画的实现(一)
19. UIKit框架(十九) —— 基于CALayer属性的一种3D边栏动画的实现(二)
20. UIKit框架(二十) —— 基于UILabel跑马灯类似效果的实现(一)
21. UIKit框架(二十一) —— UIStackView的使用(一)
22. UIKit框架(二十二) —— 基于UIPresentationController的自定义viewController的转场和展示(一)
23. UIKit框架(二十三) —— 基于UIPresentationController的自定义viewController的转场和展示(二)
24. UIKit框架(二十四) —— 基于UICollectionViews和Drag-Drop在两个APP间的使用示例 (一)
25. UIKit框架(二十五) —— 基于UICollectionViews和Drag-Drop在两个APP间的使用示例 (二)
26. UIKit框架(二十六) —— UICollectionView的自定义布局 (一)
27. UIKit框架(二十七) —— UICollectionView的自定义布局 (二)
28. UIKit框架(二十八) —— 一个UISplitViewController的简单实用示例 (一)
29. UIKit框架(二十九) —— 一个UISplitViewController的简单实用示例 (二)
30. UIKit框架(三十) —— 基于UICollectionViewCompositionalLayout API的UICollectionViews布局的简单示例(一)
31. UIKit框架(三十一) —— 基于UICollectionViewCompositionalLayout API的UICollectionViews布局的简单示例(二)
32. UIKit框架(三十二) —— 替换Peek and Pop交互的基于iOS13的Context Menus(一)
33. UIKit框架(三十三) —— 替换Peek and Pop交互的基于iOS13的Context Menus(二)
34. UIKit框架(三十四) —— Accessibility的使用(一)
35. UIKit框架(三十五) —— Accessibility的使用(二)
36. UIKit框架(三十六) —— UICollectionView UICollectionViewDiffableDataSource的使用(一)
37. UIKit框架(三十七) —— UICollectionView UICollectionViewDiffableDataSource的使用(二)
38. UIKit框架(三十八) —— 基于CollectionView转盘效果的实现(一)
39. UIKit框架(三十九) —— iOS 13中UISearchController 和 UISearchBar的新更改(一)
40. UIKit框架(四十) —— iOS 13中UISearchController 和 UISearchBar的新更改(二)
41. UIKit框架(四十一) —— 使用协议构建自定义Collection(一)
42. UIKit框架(四十二) —— 使用协议构建自定义Collection(二)
43. UIKit框架(四十三) —— CALayer的简单实用示例(一)
44. UIKit框架(四十四) —— CALayer的简单实用示例(二)
45. UIKit框架(四十五) —— 支持DarkMode的简单示例(一)
46. UIKit框架(四十六) —— 支持DarkMode的简单示例(二)
47. UIKit框架(四十七) —— 自定义Calendar Control的简单示例(一)
48. UIKit框架(四十八) —— 自定义Calendar Control的简单示例(二)
49. UIKit框架(四十九) —— UIVisualEffectView原理和简单使用(一)
50. UIKit框架(五十) —— UIVisualEffectView原理和简单使用(二)
开始
首先看下主要内容:
在本教程中,您将学习如何在单个
collection view
中创建列表,使用现代单元配置(cell configuration)
以及配置多个section snapshot
。内容来自翻译。
接着看下写作环境:
Swift 5, iOS 14, Xcode 12
下面就一起来看正文啦。
在iOS 14中,Apple为UICollectionView
引入了新特性。Lists使您可以在UICollectionView
中包括类似UITableView
的部分。现代单元配置Modern Cell Configuration使注册和配置collection view cell
更加容易。并且,Section Snapshots允许UICollectionView
中包含多个section
,其中每个section
可以具有不同的布局。
在本教程中,您将学习如何:
- 使用
UICollectionLayoutListConfiguration
创建一个可扩展列表list
。 - 使用
Modern Cell Configuration
来配置UICollectionView cell
。 - 使用
Section Snapshots
可将多个节(section)
添加到UICollectionView
。
注意:本教程假定您熟悉Apple在iOS 13中引入的
UICollectionViewDiffableDataSource
和UICollectionViewCompositionalLayout
。如果您以前从未使用过它们,请查看Collection View and Diffable Data Source and Modern Collection Views with Compositional Layouts。
事不宜迟,该开始了!
在Xcode
中打开启动项目。构建并运行。
您会看到一个空的Pet Explorer
屏幕。 它是Get a Pet
应用程序的一部分,该应用程序显示可供收养的宠物。 您将基于此应用程序构建。 在最终版本中,您可以浏览宠物类别并选择一个宠物以查看其详细信息。 然后,当您找到喜欢的宠物时,可以点击Adopt以领养该宠物。
完整的应用程序的Pet Explorer
屏幕显示可用和已收养的宠物:
注意可爱的狗Diego
。完成本教程后,您将成为该虚拟小狗的骄傲拥有者。
打开Xcode
。浏览项目。当应用启动时,它使用PetExplorerViewController
作为根视图控制器,将导航控制器设置为初始视图控制器。打开Main.storyboard
选中该设置。
打开PetExplorerViewController.swift
以浏览此文件。 PetExplorerViewController
的collectionView
为空。稍后,您将使用代表宠物和宠物类别的列表项来填充它。
Pet.swift
具有与宠物有关的所有数据。
DataSource
类型别名是为了方便起见。稍后在配置UICollectionView
数据源时将使用它。
枚举Section
代表.availablePets
和.adoptedPets
的UICollectionView
部分。
最后,在PetExplorerViewController
扩展程序中,您会找到pushDetailForPet(_:withAdoptionStatus :)
。当用户选择一个项目时,此方法将显示PetDetailViewController
。
打开PetDetailViewController.swift
。这是一个简单的视图控制器类,可显示宠物的图像,姓名和出生年月。
现在,您已经探索了应用程序的结构,接下来是时候了解UICollectionView
列表了。
What is a List?
list
是UICollectionView
中的table view
类似的视图。您可以通过仅在少量代码下将可配置的UICollectionViewCompositionalLayout
应用于UICollectionView
的一section
来创建列表。
您可以配置列表以显示分层数据,并可以折叠和展开列表项或使其看起来类似于传统的table view
。如果您需要在应用程序中使用table view
,则可以使用带有UICollectionView API
的列表,也可以使用传统的UITableView
。
在大多数情况下,列表list
更易于创建和配置。
现在该创建您的第一个列表list
了。
Creating a List
您将创建一个显示宠物类别的平面列表。这将是您不使用UITableView
的第一个table view
。对于平面列表,与UITableView
相比,UICollectionView list
的优势可能不会立即显现。稍后,当您使列表可扩展时,您会发现使用UICollectionView list
的真正好处。
注意:
UICollectionView
体系结构在布局,表示形式和数据之间有清晰的分隔。本教程的示例代码遵循这种模式。每次向Get a Pet添加新功能时,您都会添加一段代码,首先用于布局,然后用于演示,最后用于数据。
1. Configuring the Layout
在iOS 13
中,Apple引入了UICollectionViewCompositionalLayout
,这是一种用于构建复杂布局的新API。 在iOS 14
中,Apple添加了:
static func list(using configuration: UICollectionLayoutListConfiguration) ->
UICollectionViewCompositionalLayout
这使您可以在一行代码中创建list layout
,而无需了解UICollectionViewCompositionalLayout API
的详细知识。 您可以使用UICollectionLayoutListConfiguration
配置列表的外观,颜色,分隔符,页眉和页脚。
现在该将其应用于您的代码了:
打开PetExplorerViewController.swift
。 在带有// MARK: - Functions
的行下方添加以下方法:
func configureLayout() {
// 1
let configuration = UICollectionLayoutListConfiguration(appearance: .grouped)
// 2
collectionView.collectionViewLayout =
UICollectionViewCompositionalLayout.list(using: configuration)
}
这将配置collectionView
的布局。 在这里,您:
- 1) 创建具有
.grouped
外观的configuration
。 这为您提供了一个看起来像table view
的布局配置。 - 2) 接下来,您将创建一个具有
list section
的UICollectionViewCompositionalLayout
,该节section
将使用该configuration
。 您需要将此布局应用于collectionView
。
如您所见,整个布局配置只有两行代码。
通过添加以下内容在viewDidLoad()
的末尾调用此方法:
configureLayout()
2. Configuring the Presentation
现在是时候为列表list
创建一个collection view cell
了。 单元格显示宠物类别。 您将了解注册单元格的新方法。
在第一个PetExplorerViewController
扩展块内,添加:
// 1
func categoryCellregistration() ->
UICollectionView.CellRegistration<UICollectionViewListCell, Item> {
// 2
return .init { cell, _, item in
// 3
var configuration = cell.defaultContentConfiguration()
configuration.text = item.title
cell.contentConfiguration = configuration
}
}
这是您首次接触modern cell registration and configuration
。 代码是这样的:
- 1)
categoryCellregistration()
为UICollectionViewListCell
类型的单元格和Item
类型的数据项创建单元格注册。 这是注册collection view cell
的现代方法。 - 2) 您创建单元格注册,并传入一个闭包来配置单元格。 当
cell
需要渲染时调用闭包。 - 3) 然后配置
cell
。 宠物类别在item.title
中可用。 如果您不了解正在发生的事情,请不要担心。 本教程的整个章节都涉及现代单元配置(modern cell configuration)
。
在配置数据源时,您将调用categoryCellregistration()
。
3. Configuring the Data
您已为collection view
配置了布局和单元格。 现在,您需要一种机制,可以基于collection view
的基础数据创建这些单元格。 这就是数据源的来源。
将以下方法添加到PetExplorerViewController
中:
func makeDataSource() -> DataSource {
// 1
return DataSource(collectionView: collectionView) {
collectionView, indexPath, item -> UICollectionViewCell? in
// 2
return collectionView.dequeueConfiguredReusableCell(
using: self.categoryCellregistration(), for: indexPath, item: item)
}
}
这是您所做的:
- 1) 您创建并返回一个
DataSource
,传入collectionView
和一个闭包,该闭包将UICollectionViewCell
提供给数据源。 - 2) 在闭包内部,您要求
collectionView
使UICollectionViewCell
出dequeue
。 然后,您将单元格注册作为参数传递,因此collectionView
将知道其必须出队的单元格类型。 您刚才创建的categoryCellregistration()
包含单元配置的逻辑。
将以下属性添加到PetExplorerViewController
:
lazy var dataSource = makeDataSource()
由于您在声明中使用了lazy
,因此在第一次需要collectionView
时会为其创建数据源。
您配置了collectionView
的布局,展示和数据。 现在,您将使用数据项填充collectionView
。
仍在PetExplorerViewController.swift
中,将以下方法添加到PetExplorerViewController
中:
func applyInitialSnapshots() {
// 1
var categorySnapshot = NSDiffableDataSourceSnapshot<Section, Item>()
// 2
let categories = Pet.Category.allCases.map { category in
return Item(title: String(describing: category))
}
// 3
categorySnapshot.appendSections([.availablePets])
// 4
categorySnapshot.appendItems(categories, toSection: .availablePets)
// 5
dataSource.apply(categorySnapshot, animatingDifferences: false)
}
此代码使用可扩散的数据源来更新list
的内容。 Apple在iOS 13
中引入了diffable data source
。该代码尚无任何新的iOS 14
功能。 当您使列表可扩展并将section snapshots
添加到列表时,情况将会改变。
使用applyInitialSnapshots()
可以:
- 1) 创建一个
categorySnapshot
,其中包含宠物类别名称。 - 2) 然后为每个类别
category
创建一个项目Item
并将其添加到categories
中。 - 3) 将
.availablePets
附加到categorySnapshot
。 - 4) 然后将类别
categories
中的项目附加到categorySnapshot
的.availablePets
。 - 5) 将
categorySnapshot
应用于dataSource
。
您已经添加了一个部分section
,并指明了属于该部分的所有元素。
现在,在viewDidLoad()
的末尾添加对applyInitialSnapshots()
的调用:
applyInitialSnapshots()
构建并运行
恭喜你! 这是您的第一个带有列表的UICollectionView
。
列表list
支持与UITableView
样式匹配的外观:.plain,.grouped
和.insetGrouped
。 您创建的列表具有.grouped
外观。
iOS 14
具有用于将列表显示为边栏的新外观:.sidebar
和.sidebarPlain
。 它们通常在拆分视图(split view)
中用作主视图。
现在,您可以扩展列表。
Making the List Expandable
现在是时候将pets
添加到类别中了。
在这里,您将发现UICollectionView
列表的强大功能。 使用UITableView
,您将不得不处理category cells and pet cell
上的点击tap
,保持单元格的可见状态和展开状态,并编写显示或隐藏宠物单元格的代码。
使用UICollectionView
列表,您只需要提供类别和宠物的分层数据结构。 该列表将处理其余的内容。 您很快就会发现,仅需几行代码就可以实现很多目标。
Pet.swift
包含所有宠物及其所属类别的数据。 无需更改布局中的任何内容,因此您将从展示开始。
1. Configuring the Presentation
之前,您为pet category
创建了一个单元格。 您了解了注册单元格的新方法。 在这里,您将执行相同的操作,这次将为宠物创建一个cell
。 单元格将显示宠物的名字。
在PetExplorerViewController.swift
中,添加:
func petCellRegistration() ->
UICollectionView.CellRegistration<UICollectionViewListCell, Item> {
return .init { cell, _, item in
guard let pet = item.pet else {
return
}
var configuration = cell.defaultContentConfiguration()
configuration.text = pet.name
cell.contentConfiguration = configuration
}
}
petCellRegistration()
与您之前添加的categoryCellregistration()
类似。 您创建一个单元注册并使用现代单元配置来配置该单元。 在这里,您使用defaultContentConfiguration()
,然后将宠物名称分配为要显示的文本。
在配置数据源时,您将调用petCellRegistration()
。
接下来,您可以通过向类别单元格添加outline disclosure accessory
来扩展列表。 这表明一个项目可以展开和折叠。 当您点击一个类别时,列表将展开并显示该类别的宠物。
在categoryCellregistration()
中,在cell.contentConfiguration = configuration
下方,添加:
// 1
let options = UICellAccessory.OutlineDisclosureOptions(style: .header)
// 2
let disclosureAccessory = UICellAccessory.outlineDisclosure(options: options)
// 3
cell.accessories = [disclosureAccessory]
在这里,您:
- 1) 创建要应用于
disclosureAccessory
的options
。 您使用.header
样式使单元格可扩展。 - 2) 然后,使用配置的
options
创建一个disclosureAccessory
。 - 3) 将
accessory
应用于cell
。 单元cell
可以具有多个附件,因此您可以在一个数组中添加discoveryAccessory
。
构建并运行。
outline disclosure
是可见的,但是当您点击一个单元格时,什么也不会发生。 为什么? 您尚未将宠物添加到其类别中。 接下来,您将要执行此操作。
2. Configuring the Data
接下来,您将学习如何将分层数据添加到列表中。 完成后,您会看到列表自动支持折叠和展开单元格。
现在,调整数据源以将宠物细胞添加到其类别中。
在makeDatasource()
中,替换为:
return collectionView.dequeueConfiguredReusableCell(
using: self.categoryCellregistration(), for: indexPath, item: item)
为
if item.pet != nil {
// 1
return collectionView.dequeueConfiguredReusableCell(
using: self.petCellRegistration(), for: indexPath, item: item)
} else {
// 2
return collectionView.dequeueConfiguredReusableCell(
using: self.categoryCellregistration(), for: indexPath, item: item)
}
一个item
可以代表一个类别或一个宠物。 这取决于pet
的值。 在此代码中,collectionView
将dequeue
:
- 1) 如果
item.pet
不为nil
,则为pet
的cell
。 - 2) 如果
item.pet
为nil
,则为category
的cell
。
您已配置了展示宠物所需的一切,但尚未添加任何宠物。 为此,您必须更新数据的初始快照。
将body
替换为applyInitialSnapshots()
:
// 1
var categorySnapshot = NSDiffableDataSourceSectionSnapshot<Item>()
// 2
for category in Pet.Category.allCases {
// 3
let categoryItem = Item(title: String(describing: category))
// 4
categorySnapshot.append([categoryItem])
// 5
let petItems = category.pets.map { Item(pet: $0, title: $0.name) }
// 6
categorySnapshot.append(petItems, to: categoryItem)
}
// 7
dataSource.apply(
categorySnapshot,
to: .availablePets,
animatingDifferences: false)
要在类别和宠物之间建立层次关系,请执行以下操作:
- 1) 创建类型为
NSDiffableDataSourceSectionSnapshot
的categorySnapshot
。 这是一个section snapshot
。 使用section snapshot
,可以用分层结构表示数据,例如带有可扩展项的轮廓。目前,这是有关section snapshot
的全部知识。 在本教程的后面,您将了解有关section snapshot
的更多信息。 - 2) 然后,遍历
Pet.Category.allCases
中的类别。 在循环中,将宠物添加到其类别中。 - 3) 创建一个
categoryItem
。 - 4) 将
categoryItem
追加到categorySnapshot
。 - 5) 然后,创建一个数组
petItems
,其中包含属于当前类别category
的所有宠物。 - 6) 通过将
petItems
附加到当前categoryItem
来创建类别和宠物之间的层次关系。 - 7) 将
categorySnapshot
应用于dataSource
的.availablePets
。
构建并运行。
点击一个类别。列表将展开并显示宠物名称。很好!
现在是时候让cell
看起来更好一点了。
What is Modern Cell Configuration?
如果您使用过UITableView
或UICollectionView
,则习惯于通过直接设置单元格的属性来对其进行配置。在iOS 14
中,单元配置可以完全与单元本身分离。
您创建类型为UIContentConfiguration
的cell content configuration
。然后,根据需要设置此内容配置的属性。同样,您可以创建类型为UIBackgroundConfiguration
的cell background configuration
。
结果是可重用的配置,您可以将其应用于您喜欢的任何cell
。
现在该看看它是如何工作的!
Configuring the Cells
您刚刚学习了modern cell configuration
的理论。现在,您将通过添加代码来更新pet cell
以显示宠物的图像和年龄,从而实施单元格内容配置。在下一部分中,您将应用cell background configuration
。
在petCellRegistration()
中,替换:
var configuration = cell.defaultContentConfiguration()
configuration.text = pet.name
cell.contentConfiguration = configuration
为
// 1
var configuration = cell.defaultContentConfiguration()
// 2
configuration.text = pet.name
configuration.secondaryText = "\(pet.age) years old"
configuration.image = UIImage(named: pet.imageName)
// 3
configuration.imageProperties.maximumSize = CGSize(width: 40, height: 40)
// 4
cell.contentConfiguration = configuration
在这里,您将看到实际的单元格内容配置。 您:
- 1) 使用默认样式创建类型
UIListContentConfiguration
的配置。使用此配置,您将在一个单元格中设置图像,文本和辅助文本。 - 2) 将宠物的数据应用于配置,包括宠物的图像。
- 3) 设置图像的尺寸。
- 4) 将
configuration
应用于单元的contentConfiguration
。
构建并运行。
突然,宠物看起来更可爱了。 您是否有动机领养个?
Adopting a Pet
最后,您领养一只宠物。 Diego
正在等你接他!
首先,您将学习如何为单元创建和应用后台配置。 养有宠物的牢Cells
将具有背景色。 您将使用iOS 14
中引入的UIBackgroundConfiguration
,它是现代单元配置的一部分。
入门项目已经具有存储被收养宠物的属性:adoptions
。
在petCellRegistration()
和cell.contentConfiguration = configuration
下,添加:
// 1
if self.adoptions.contains(pet) {
// 2
var backgroundConfig = UIBackgroundConfiguration.listPlainCell()
// 3
backgroundConfig.backgroundColor = .systemBlue
backgroundConfig.cornerRadius = 5
backgroundConfig.backgroundInsets = NSDirectionalEdgeInsets(
top: 5, leading: 5, bottom: 5, trailing: 5)
// 4
cell.backgroundConfiguration = backgroundConfig
}
要为单元格提供彩色背景,请执行以下操作:
- 1) 检查宠物是否被收养。 只有收养的宠物才会有彩色背景。
- 2) 创建一个
UIBackgroundConfiguration
,为listPlainCell
配置默认属性。 将其分配给backgroundConfig
。 - 3) 接下来,根据您的喜好修改
backgroundConfig
。 - 4) 将
backgroundConfig
分配给cell.backgroundConfiguration
。
您尚无法测试。 您需要先养宠物。
入门项目有一个PetDetailViewController
。 该视图控制器具有Adopt
按钮。 但是,如何导航到PetDetailViewController
?
您向宠物cell
添加一个disclosure indicator
。 在petCellRegistration()
和cell.contentConfiguration = configuration
下,添加:
cell.accessories = [.disclosureIndicator()]
在这里设置单元格的disclosure indicator
。
现在,当您点击宠物单元格时,您需要导航到Pet DetailViewController
。
将以下代码添加到collectionView(_:didSelectItemAt :)
// 1
guard let item = dataSource.itemIdentifier(for: indexPath) else {
collectionView.deselectItem(at: indexPath, animated: true)
return
}
// 2
guard let pet = item.pet else {
return
}
// 3
pushDetailForPet(pet, withAdoptionStatus: adoptions.contains(pet))
当您点击pet cell
时,将调用collectionView(_:didSelectItemAt :)
。 在此代码中,您:
- 1) 检查所选
indexPath
处的item
是否存在。 - 2) 安全解包
pet
。 - 3) 然后,将
PetDetailViewController
推入导航堆栈。pushDetailForPet()
是入门项目的一部分。
构建并运行。 寻找Diego
,然后点按单元格。
这是你的朋友Diego
! 点击Adopt
按钮。
您已采用Diego
,然后导航回Pet Explorer
。 您会期望Diego
的单元格具有蓝色背景,但背景并非如此。 发生了什么?
数据源尚未更新。 您现在就要做。
将以下方法添加到PetExplorerViewController
中:
func updateDataSource(for pet: Pet) {
// 1
var snapshot = dataSource.snapshot()
let items = snapshot.itemIdentifiers
// 2
let petItem = items.first { item in
item.pet == pet
}
if let petItem = petItem {
// 3
snapshot.reloadItems([petItem])
// 4
dataSource.apply(snapshot, animatingDifferences: true, completion: nil)
}
}
在此代码中,您:
- 1) 从
dataSource.snapshot()
中检索所有items
。 - 2) 查找代表
pet
的item
并将其分配给petItem
。 - 3) 在
snapshot
中重新加载petItem
。 - 4) 然后将更新的
snapshot
应用于dataSource
。
现在确保收养宠物时调用updateDataSource(for :)
。
在petDetailViewController(_:didAdoptPet :)
中,添加:
// 1
adoptions.insert(pet)
// 2
updateDataSource(for: pet)
用户收养宠物时,将调用此代码。 在这里:
- 1) 将收养的
pet
插入adoptions
。 - 2) 调用
updateDataSource(for :)
。 这是您刚创建的方法。
构建并运行。 点击Diego
。 然后,在详细信息屏幕上,点击Adopt
。 向后浏览后,您会看到以下屏幕。
Diego
有蓝色背景。 他现在是你的。
What is a Section Snapshot?
section snapshot
将UICollectionView
中单个section
的数据封装起来。 这有两个重要的好处:
- 1)
Section snapshot
使model hierarchical data
成为可能。 在实施带有宠物类别的列表时,您已经应用了此功能。 - 2)
UICollectionView
数据源每个section
可以有一个snapshot
,而不是整个collection view
的单个快照。 这使您可以将多个sections
添加到collection view
中,其中每个部分可以具有不同的布局和行为。
您将为收养的宠物添加一个section
,以了解其工作原理。
Adding a Section for Adopted Pets
您想在collectionView
中的单独section
中创建收养的宠物列表,其中包含您之前创建的宠物类别的可扩展列表下方。
1. Configuring the Layout
用以下命令替换configureLayout()
的body
:
// 1
let provider =
{(_: Int, layoutEnv: NSCollectionLayoutEnvironment) ->
NSCollectionLayoutSection? in
// 2
let configuration = UICollectionLayoutListConfiguration(
appearance: .grouped)
// 3
return NSCollectionLayoutSection.list(
using: configuration,
layoutEnvironment: layoutEnv)
}
// 4
collectionView.collectionViewLayout =
UICollectionViewCompositionalLayout(sectionProvider: provider)
这样可以按部分section
配置collectionView
的布局。 在此代码中,您:
- 1) 创建一个返回
NSCollectionLayoutSection
的闭包。 现在您有多个sections
,并且此闭包可以根据sectionIndex
分别返回每个section
的布局。 在这种情况下,各section
的布局相同,因此您无需使用sectionIndex
。您将闭包分配给provider
。layoutEnv
提供有关布局环境的信息。 - 2) 为具有
.grouped
外观的列表创建配置。 - 3) 返回具有给定
configuration
的section
的NSCollectionLayoutSection.list
。 - 4) 使用
provider
作为sectionProvider
创建UICollectionViewCompositionalLayout
。 您将布局分配给collectionView.collectionViewLayout
。
接下来,您将配置展示。
2. Configuring the Presentation
将以下方法添加到第一个PetExplorerViewController
扩展块中:
func adoptedPetCellRegistration()
-> UICollectionView.CellRegistration<UICollectionViewListCell, Item> {
return .init { cell, _, item in
guard let pet = item.pet else {
return
}
var configuration = cell.defaultContentConfiguration()
configuration.text = "Your pet: \(pet.name)"
configuration.secondaryText = "\(pet.age) years old"
configuration.image = UIImage(named: pet.imageName)
configuration.imageProperties.maximumSize = CGSize(width: 40, height: 40)
cell.contentConfiguration = configuration
cell.accessories = [.disclosureIndicator()]
}
}
此代码将应用于.adoptedPets
中的单元格。 它对您应该看起来很熟悉。 与您在Configuring the Cells
中添加的petCellRegistration()
类似。 现在,您将配置数据。
3. Configuring the Data
在makeDatasource()
中,替换:
return collectionView.dequeueConfiguredReusableCell(
using: self.petCellRegistration(), for: indexPath, item: item)
为
// 1
guard let section = Section(rawValue: indexPath.section) else {
return nil
}
switch section {
// 2
case .availablePets:
return collectionView.dequeueConfiguredReusableCell(
using: self.petCellRegistration(), for: indexPath, item: item)
// 3
case .adoptedPets:
return collectionView.dequeueConfiguredReusableCell(
using: self.adoptedPetCellRegistration(), for: indexPath, item: item)
}
使用此代码,可以使数据源返回的单元格依赖于该section
。 在这里:
- 1) 安全地解包
section
。 - 2) 返回
.availablePets
的petCellRegistration()
- 3) 返回
.adoptedPets
的enabledPetCellRegistration()
是时候将这些sections
添加到数据源中了。
在applyInitialSnapshots()
中,在方法的开头插入以下代码:
// 1
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
// 2
snapshot.appendSections(Section.allCases)
// 3
dataSource.apply(snapshot, animatingDifferences: false)
在此代码中,您:
- 1) 创建一个新的
snapshot
。 - 2) 将所有
sections
追加到snapshot
。 - 3) 将
snapshot
应用于dataSource
。
构建并运行。 领养Diego
。
Diego
的背景是蓝色,所以您知道收养成功了。 但是您添加的section
在哪里?
该section
在那里,但为空。 您已将Diego
添加到了收养的宠物中,但尚未将他插入数据源中。 这就是您现在要做的。
在petDetailViewController(_:didAdoptPet :)
中,在options.insert(pet)
的正下方,添加:
// 1
var adoptedPetsSnapshot = dataSource.snapshot(for: .adoptedPets)
// 2
let newItem = Item(pet: pet, title: pet.name)
// 3
adoptedPetsSnapshot.append([newItem])
// 4
dataSource.apply(
adoptedPetsSnapshot,
to: .adoptedPets,
animatingDifferences: true,
completion: nil)
使用此代码,您:
- 1) 从数据源中检索
.adoptedPets
的snapshot
。 您将其分配给adoptedPetsSnapshot
。 - 2) 为收养的
pet
创建一个新Item
,并将其分配给newItem
。 - 3) 将
newItem
追加到adoptedPetsSnapshot
。 - 4) 您将修改后的
acceptedPetsSnapshot
应用于dataSource
的.adoptedPets
。
构建并运行。
起作用了! Diego
在收养宠物部分。
您已经了解了iOS 14
中UICollectionView
的许多改进,其中包括:
- 1) 使用
UICollectionLayoutListConfiguration
创建可扩展list
。 - 2) 使用
Modern Cell Configuration
来配置UICollectionView
单元。 - 3) 使用
Section Snapshots
可将多个sections
添加到UICollectionView
。
而您只接触了表面。 有关更多详细信息,请查看WWDC 2020中的Advances in UICollectionView。
后记
本篇主要讲述了基于
iOS14
的UICollectionView List
的创建,感兴趣的给个赞或者关注~~~
网友评论