版本記錄
版本號(hào) | 時(shí)間 |
---|---|
V1.0 | 2021.01.25 星期一 |
前言
iOS中有關(guān)視圖控件用戶能看到的都在UIKit框架里面,用戶交互也是通過(guò)UIKit進(jìn)行的。感興趣的參考上面幾篇文章。
1. UIKit框架(一) —— UIKit動(dòng)力學(xué)和移動(dòng)效果(一)
2. UIKit框架(二) —— UIKit動(dòng)力學(xué)和移動(dòng)效果(二)
3. UIKit框架(三) —— UICollectionViewCell的擴(kuò)張效果的實(shí)現(xiàn)(一)
4. UIKit框架(四) —— UICollectionViewCell的擴(kuò)張效果的實(shí)現(xiàn)(二)
5. UIKit框架(五) —— 自定義控件:可重復(fù)使用的滑塊(一)
6. UIKit框架(六) —— 自定義控件:可重復(fù)使用的滑塊(二)
7. UIKit框架(七) —— 動(dòng)態(tài)尺寸UITableViewCell的實(shí)現(xiàn)(一)
8. UIKit框架(八) —— 動(dòng)態(tài)尺寸UITableViewCell的實(shí)現(xiàn)(二)
9. UIKit框架(九) —— UICollectionView的數(shù)據(jù)異步預(yù)加載(一)
10. UIKit框架(十) —— UICollectionView的數(shù)據(jù)異步預(yù)加載(二)
11. UIKit框架(十一) —— UICollectionView的重用、選擇和重排序(一)
12. UIKit框架(十二) —— UICollectionView的重用、選擇和重排序(二)
13. UIKit框架(十三) —— 如何創(chuàng)建自己的側(cè)滑式面板導(dǎo)航(一)
14. UIKit框架(十四) —— 如何創(chuàng)建自己的側(cè)滑式面板導(dǎo)航(二)
15. UIKit框架(十五) —— 基于自定義UICollectionViewLayout布局的簡(jiǎn)單示例(一)
16. UIKit框架(十六) —— 基于自定義UICollectionViewLayout布局的簡(jiǎn)單示例(二)
17. UIKit框架(十七) —— 基于自定義UICollectionViewLayout布局的簡(jiǎn)單示例(三)
18. UIKit框架(十八) —— 基于CALayer屬性的一種3D邊欄動(dòng)畫(huà)的實(shí)現(xiàn)(一)
19. UIKit框架(十九) —— 基于CALayer屬性的一種3D邊欄動(dòng)畫(huà)的實(shí)現(xiàn)(二)
20. UIKit框架(二十) —— 基于UILabel跑馬燈類似效果的實(shí)現(xiàn)(一)
21. UIKit框架(二十一) —— UIStackView的使用(一)
22. UIKit框架(二十二) —— 基于UIPresentationController的自定義viewController的轉(zhuǎn)場(chǎng)和展示(一)
23. UIKit框架(二十三) —— 基于UIPresentationController的自定義viewController的轉(zhuǎn)場(chǎng)和展示(二)
24. UIKit框架(二十四) —— 基于UICollectionViews和Drag-Drop在兩個(gè)APP間的使用示例 (一)
25. UIKit框架(二十五) —— 基于UICollectionViews和Drag-Drop在兩個(gè)APP間的使用示例 (二)
26. UIKit框架(二十六) —— UICollectionView的自定義布局 (一)
27. UIKit框架(二十七) —— UICollectionView的自定義布局 (二)
28. UIKit框架(二十八) —— 一個(gè)UISplitViewController的簡(jiǎn)單實(shí)用示例 (一)
29. UIKit框架(二十九) —— 一個(gè)UISplitViewController的簡(jiǎn)單實(shí)用示例 (二)
30. UIKit框架(三十) —— 基于UICollectionViewCompositionalLayout API的UICollectionViews布局的簡(jiǎn)單示例(一)
31. UIKit框架(三十一) —— 基于UICollectionViewCompositionalLayout API的UICollectionViews布局的簡(jiǎn)單示例(二)
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轉(zhuǎn)盤效果的實(shí)現(xiàn)(一)
39. UIKit框架(三十九) —— iOS 13中UISearchController 和 UISearchBar的新更改(一)
40. UIKit框架(四十) —— iOS 13中UISearchController 和 UISearchBar的新更改(二)
41. UIKit框架(四十一) —— 使用協(xié)議構(gòu)建自定義Collection(一)
42. UIKit框架(四十二) —— 使用協(xié)議構(gòu)建自定義Collection(二)
43. UIKit框架(四十三) —— CALayer的簡(jiǎn)單實(shí)用示例(一)
44. UIKit框架(四十四) —— CALayer的簡(jiǎn)單實(shí)用示例(二)
45. UIKit框架(四十五) —— 支持DarkMode的簡(jiǎn)單示例(一)
46. UIKit框架(四十六) —— 支持DarkMode的簡(jiǎn)單示例(二)
47. UIKit框架(四十七) —— 自定義Calendar Control的簡(jiǎn)單示例(一)
48. UIKit框架(四十八) —— 自定義Calendar Control的簡(jiǎn)單示例(二)
49. UIKit框架(四十九) —— UIVisualEffectView原理和簡(jiǎn)單使用(一)
50. UIKit框架(五十) —— UIVisualEffectView原理和簡(jiǎn)單使用(二)
開(kāi)始
首先看下主要內(nèi)容:
在本教程中,您將學(xué)習(xí)如何在單個(gè)
collection view
中創(chuàng)建列表,使用現(xiàn)代單元配置(cell configuration)
以及配置多個(gè)section snapshot
。內(nèi)容來(lái)自翻譯。
接著看下寫作環(huán)境:
Swift 5, iOS 14, Xcode 12
下面就一起來(lái)看正文啦。
在iOS 14中,Apple為UICollectionView
引入了新特性。Lists使您可以在UICollectionView
中包括類似UITableView
的部分。現(xiàn)代單元配置Modern Cell Configuration使注冊(cè)和配置collection view cell
更加容易。并且,Section Snapshots允許UICollectionView
中包含多個(gè)section
,其中每個(gè)section
可以具有不同的布局。
在本教程中,您將學(xué)習(xí)如何:
- 使用
UICollectionLayoutListConfiguration
創(chuàng)建一個(gè)可擴(kuò)展列表list
。 - 使用
Modern Cell Configuration
來(lái)配置UICollectionView cell
。 - 使用
Section Snapshots
可將多個(gè)節(jié)(section)
添加到UICollectionView
。
注意:本教程假定您熟悉Apple在iOS 13中引入的
UICollectionViewDiffableDataSource
和UICollectionViewCompositionalLayout
。如果您以前從未使用過(guò)它們,請(qǐng)查看Collection View and Diffable Data Source and Modern Collection Views with Compositional Layouts。
事不宜遲,該開(kāi)始了!
在Xcode
中打開(kāi)啟動(dòng)項(xiàng)目。構(gòu)建并運(yùn)行。
您會(huì)看到一個(gè)空的Pet Explorer
屏幕。 它是Get a Pet
應(yīng)用程序的一部分,該應(yīng)用程序顯示可供收養(yǎng)的寵物。 您將基于此應(yīng)用程序構(gòu)建。 在最終版本中,您可以瀏覽寵物類別并選擇一個(gè)寵物以查看其詳細(xì)信息。 然后,當(dāng)您找到喜歡的寵物時(shí),可以點(diǎn)擊Adopt以領(lǐng)養(yǎng)該寵物。
完整的應(yīng)用程序的Pet Explorer
屏幕顯示可用和已收養(yǎng)的寵物:
注意可愛(ài)的狗Diego
。完成本教程后,您將成為該虛擬小狗的驕傲擁有者。
打開(kāi)Xcode
。瀏覽項(xiàng)目。當(dāng)應(yīng)用啟動(dòng)時(shí),它使用PetExplorerViewController
作為根視圖控制器,將導(dǎo)航控制器設(shè)置為初始視圖控制器。打開(kāi)Main.storyboard
選中該設(shè)置。
打開(kāi)PetExplorerViewController.swift
以瀏覽此文件。 PetExplorerViewController
的collectionView
為空。稍后,您將使用代表寵物和寵物類別的列表項(xiàng)來(lái)填充它。
Pet.swift
具有與寵物有關(guān)的所有數(shù)據(jù)。
DataSource
類型別名是為了方便起見(jiàn)。稍后在配置UICollectionView
數(shù)據(jù)源時(shí)將使用它。
枚舉Section
代表.availablePets
和.adoptedPets
的UICollectionView
部分。
最后,在PetExplorerViewController
擴(kuò)展程序中,您會(huì)找到pushDetailForPet(_:withAdoptionStatus :)
。當(dāng)用戶選擇一個(gè)項(xiàng)目時(shí),此方法將顯示PetDetailViewController
。
打開(kāi)PetDetailViewController.swift
。這是一個(gè)簡(jiǎn)單的視圖控制器類,可顯示寵物的圖像,姓名和出生年月。
現(xiàn)在,您已經(jīng)探索了應(yīng)用程序的結(jié)構(gòu),接下來(lái)是時(shí)候了解UICollectionView
列表了。
What is a List?
list
是UICollectionView
中的table view
類似的視圖。您可以通過(guò)僅在少量代碼下將可配置的UICollectionViewCompositionalLayout
應(yīng)用于UICollectionView
的一section
來(lái)創(chuàng)建列表。
您可以配置列表以顯示分層數(shù)據(jù),并可以折疊和展開(kāi)列表項(xiàng)或使其看起來(lái)類似于傳統(tǒng)的table view
。如果您需要在應(yīng)用程序中使用table view
,則可以使用帶有UICollectionView API
的列表,也可以使用傳統(tǒng)的UITableView
。
在大多數(shù)情況下,列表list
更易于創(chuàng)建和配置。
現(xiàn)在該創(chuàng)建您的第一個(gè)列表list
了。
Creating a List
您將創(chuàng)建一個(gè)顯示寵物類別的平面列表。這將是您不使用UITableView
的第一個(gè)table view
。對(duì)于平面列表,與UITableView
相比,UICollectionView list
的優(yōu)勢(shì)可能不會(huì)立即顯現(xiàn)。稍后,當(dāng)您使列表可擴(kuò)展時(shí),您會(huì)發(fā)現(xiàn)使用UICollectionView list
的真正好處。
注意:
UICollectionView
體系結(jié)構(gòu)在布局,表示形式和數(shù)據(jù)之間有清晰的分隔。本教程的示例代碼遵循這種模式。每次向Get a Pet添加新功能時(shí),您都會(huì)添加一段代碼,首先用于布局,然后用于演示,最后用于數(shù)據(jù)。
1. Configuring the Layout
在iOS 13
中,Apple引入了UICollectionViewCompositionalLayout
,這是一種用于構(gòu)建復(fù)雜布局的新API。 在iOS 14
中,Apple添加了:
static func list(using configuration: UICollectionLayoutListConfiguration) ->
UICollectionViewCompositionalLayout
這使您可以在一行代碼中創(chuàng)建list layout
,而無(wú)需了解UICollectionViewCompositionalLayout API
的詳細(xì)知識(shí)。 您可以使用UICollectionLayoutListConfiguration
配置列表的外觀,顏色,分隔符,頁(yè)眉和頁(yè)腳。
現(xiàn)在該將其應(yīng)用于您的代碼了:
打開(kāi)PetExplorerViewController.swift
。 在帶有// MARK: - Functions
的行下方添加以下方法:
func configureLayout() {
// 1
let configuration = UICollectionLayoutListConfiguration(appearance: .grouped)
// 2
collectionView.collectionViewLayout =
UICollectionViewCompositionalLayout.list(using: configuration)
}
這將配置collectionView
的布局。 在這里,您:
- 1) 創(chuàng)建具有
.grouped
外觀的configuration
。 這為您提供了一個(gè)看起來(lái)像table view
的布局配置。 - 2) 接下來(lái),您將創(chuàng)建一個(gè)具有
list section
的UICollectionViewCompositionalLayout
,該節(jié)section
將使用該configuration
。 您需要將此布局應(yīng)用于collectionView
。
如您所見(jiàn),整個(gè)布局配置只有兩行代碼。
通過(guò)添加以下內(nèi)容在viewDidLoad()
的末尾調(diào)用此方法:
configureLayout()
2. Configuring the Presentation
現(xiàn)在是時(shí)候?yàn)榱斜?code>list創(chuàng)建一個(gè)collection view cell
了。 單元格顯示寵物類別。 您將了解注冊(cè)單元格的新方法。
在第一個(gè)PetExplorerViewController
擴(kuò)展塊內(nèi),添加:
// 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
類型的數(shù)據(jù)項(xiàng)創(chuàng)建單元格注冊(cè)。 這是注冊(cè)collection view cell
的現(xiàn)代方法。 - 2) 您創(chuàng)建單元格注冊(cè),并傳入一個(gè)閉包來(lái)配置單元格。 當(dāng)
cell
需要渲染時(shí)調(diào)用閉包。 - 3) 然后配置
cell
。 寵物類別在item.title
中可用。 如果您不了解正在發(fā)生的事情,請(qǐng)不要擔(dān)心。 本教程的整個(gè)章節(jié)都涉及現(xiàn)代單元配置(modern cell configuration)
。
在配置數(shù)據(jù)源時(shí),您將調(diào)用categoryCellregistration()
。
3. Configuring the Data
您已為collection view
配置了布局和單元格。 現(xiàn)在,您需要一種機(jī)制,可以基于collection view
的基礎(chǔ)數(shù)據(jù)創(chuàng)建這些單元格。 這就是數(shù)據(jù)源的來(lái)源。
將以下方法添加到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) 您創(chuàng)建并返回一個(gè)
DataSource
,傳入collectionView
和一個(gè)閉包,該閉包將UICollectionViewCell
提供給數(shù)據(jù)源。 - 2) 在閉包內(nèi)部,您要求
collectionView
使UICollectionViewCell
出dequeue
。 然后,您將單元格注冊(cè)作為參數(shù)傳遞,因此collectionView
將知道其必須出隊(duì)的單元格類型。 您剛才創(chuàng)建的categoryCellregistration()
包含單元配置的邏輯。
將以下屬性添加到PetExplorerViewController
:
lazy var dataSource = makeDataSource()
由于您在聲明中使用了lazy
,因此在第一次需要collectionView
時(shí)會(huì)為其創(chuàng)建數(shù)據(jù)源。
您配置了collectionView
的布局,展示和數(shù)據(jù)。 現(xiàn)在,您將使用數(shù)據(jù)項(xiàng)填充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)
}
此代碼使用可擴(kuò)散的數(shù)據(jù)源來(lái)更新list
的內(nèi)容。 Apple在iOS 13
中引入了diffable data source
。該代碼尚無(wú)任何新的iOS 14
功能。 當(dāng)您使列表可擴(kuò)展并將section snapshots
添加到列表時(shí),情況將會(huì)改變。
使用applyInitialSnapshots()
可以:
- 1) 創(chuàng)建一個(gè)
categorySnapshot
,其中包含寵物類別名稱。 - 2) 然后為每個(gè)類別
category
創(chuàng)建一個(gè)項(xiàng)目Item
并將其添加到categories
中。 - 3) 將
.availablePets
附加到categorySnapshot
。 - 4) 然后將類別
categories
中的項(xiàng)目附加到categorySnapshot
的.availablePets
。 - 5) 將
categorySnapshot
應(yīng)用于dataSource
。
您已經(jīng)添加了一個(gè)部分section
,并指明了屬于該部分的所有元素。
現(xiàn)在,在viewDidLoad()
的末尾添加對(duì)applyInitialSnapshots()
的調(diào)用:
applyInitialSnapshots()
構(gòu)建并運(yùn)行
恭喜你! 這是您的第一個(gè)帶有列表的UICollectionView
。
列表list
支持與UITableView
樣式匹配的外觀:.plain,.grouped
和.insetGrouped
。 您創(chuàng)建的列表具有.grouped
外觀。
iOS 14
具有用于將列表顯示為邊欄的新外觀:.sidebar
和.sidebarPlain
。 它們通常在拆分視圖(split view)
中用作主視圖。
現(xiàn)在,您可以擴(kuò)展列表。
Making the List Expandable
現(xiàn)在是時(shí)候?qū)?code>pets添加到類別中了。
在這里,您將發(fā)現(xiàn)UICollectionView
列表的強(qiáng)大功能。 使用UITableView
,您將不得不處理category cells and pet cell
上的點(diǎn)擊tap
,保持單元格的可見(jiàn)狀態(tài)和展開(kāi)狀態(tài),并編寫顯示或隱藏寵物單元格的代碼。
使用UICollectionView
列表,您只需要提供類別和寵物的分層數(shù)據(jù)結(jié)構(gòu)。 該列表將處理其余的內(nèi)容。 您很快就會(huì)發(fā)現(xiàn),僅需幾行代碼就可以實(shí)現(xiàn)很多目標(biāo)。
Pet.swift
包含所有寵物及其所屬類別的數(shù)據(jù)。 無(wú)需更改布局中的任何內(nèi)容,因此您將從展示開(kāi)始。
1. Configuring the Presentation
之前,您為pet category
創(chuàng)建了一個(gè)單元格。 您了解了注冊(cè)單元格的新方法。 在這里,您將執(zhí)行相同的操作,這次將為寵物創(chuàng)建一個(gè)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()
類似。 您創(chuàng)建一個(gè)單元注冊(cè)并使用現(xiàn)代單元配置來(lái)配置該單元。 在這里,您使用defaultContentConfiguration()
,然后將寵物名稱分配為要顯示的文本。
在配置數(shù)據(jù)源時(shí),您將調(diào)用petCellRegistration()
。
接下來(lái),您可以通過(guò)向類別單元格添加outline disclosure accessory
來(lái)擴(kuò)展列表。 這表明一個(gè)項(xiàng)目可以展開(kāi)和折疊。 當(dāng)您點(diǎn)擊一個(gè)類別時(shí),列表將展開(kāi)并顯示該類別的寵物。
在categoryCellregistration()
中,在cell.contentConfiguration = configuration
下方,添加:
// 1
let options = UICellAccessory.OutlineDisclosureOptions(style: .header)
// 2
let disclosureAccessory = UICellAccessory.outlineDisclosure(options: options)
// 3
cell.accessories = [disclosureAccessory]
在這里,您:
- 1) 創(chuàng)建要應(yīng)用于
disclosureAccessory
的options
。 您使用.header
樣式使單元格可擴(kuò)展。 - 2) 然后,使用配置的
options
創(chuàng)建一個(gè)disclosureAccessory
。 - 3) 將
accessory
應(yīng)用于cell
。 單元cell
可以具有多個(gè)附件,因此您可以在一個(gè)數(shù)組中添加discoveryAccessory
。
構(gòu)建并運(yùn)行。
outline disclosure
是可見(jiàn)的,但是當(dāng)您點(diǎn)擊一個(gè)單元格時(shí),什么也不會(huì)發(fā)生。 為什么? 您尚未將寵物添加到其類別中。 接下來(lái),您將要執(zhí)行此操作。
2. Configuring the Data
接下來(lái),您將學(xué)習(xí)如何將分層數(shù)據(jù)添加到列表中。 完成后,您會(huì)看到列表自動(dòng)支持折疊和展開(kāi)單元格。
現(xiàn)在,調(diào)整數(shù)據(jù)源以將寵物細(xì)胞添加到其類別中。
在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)
}
一個(gè)item
可以代表一個(gè)類別或一個(gè)寵物。 這取決于pet
的值。 在此代碼中,collectionView
將dequeue
:
- 1) 如果
item.pet
不為nil
,則為pet
的cell
。 - 2) 如果
item.pet
為nil
,則為category
的cell
。
您已配置了展示寵物所需的一切,但尚未添加任何寵物。 為此,您必須更新數(shù)據(jù)的初始快照。
將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)
要在類別和寵物之間建立層次關(guān)系,請(qǐng)執(zhí)行以下操作:
- 1) 創(chuàng)建類型為
NSDiffableDataSourceSectionSnapshot
的categorySnapshot
。 這是一個(gè)section snapshot
。 使用section snapshot
,可以用分層結(jié)構(gòu)表示數(shù)據(jù),例如帶有可擴(kuò)展項(xiàng)的輪廓。目前,這是有關(guān)section snapshot
的全部知識(shí)。 在本教程的后面,您將了解有關(guān)section snapshot
的更多信息。 - 2) 然后,遍歷
Pet.Category.allCases
中的類別。 在循環(huán)中,將寵物添加到其類別中。 - 3) 創(chuàng)建一個(gè)
categoryItem
。 - 4) 將
categoryItem
追加到categorySnapshot
。 - 5) 然后,創(chuàng)建一個(gè)數(shù)組
petItems
,其中包含屬于當(dāng)前類別category
的所有寵物。 - 6) 通過(guò)將
petItems
附加到當(dāng)前categoryItem
來(lái)創(chuàng)建類別和寵物之間的層次關(guān)系。 - 7) 將
categorySnapshot
應(yīng)用于dataSource
的.availablePets
。
構(gòu)建并運(yùn)行。
點(diǎn)擊一個(gè)類別。列表將展開(kāi)并顯示寵物名稱。很好!
現(xiàn)在是時(shí)候讓cell
看起來(lái)更好一點(diǎn)了。
What is Modern Cell Configuration?
如果您使用過(guò)UITableView
或UICollectionView
,則習(xí)慣于通過(guò)直接設(shè)置單元格的屬性來(lái)對(duì)其進(jìn)行配置。在iOS 14
中,單元配置可以完全與單元本身分離。
您創(chuàng)建類型為UIContentConfiguration
的cell content configuration
。然后,根據(jù)需要設(shè)置此內(nèi)容配置的屬性。同樣,您可以創(chuàng)建類型為UIBackgroundConfiguration
的cell background configuration
。
結(jié)果是可重用的配置,您可以將其應(yīng)用于您喜歡的任何cell
。
現(xiàn)在該看看它是如何工作的!
Configuring the Cells
您剛剛學(xué)習(xí)了modern cell configuration
的理論。現(xiàn)在,您將通過(guò)添加代碼來(lái)更新pet cell
以顯示寵物的圖像和年齡,從而實(shí)施單元格內(nèi)容配置。在下一部分中,您將應(yīng)用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
在這里,您將看到實(shí)際的單元格內(nèi)容配置。 您:
- 1) 使用默認(rèn)樣式創(chuàng)建類型
UIListContentConfiguration
的配置。使用此配置,您將在一個(gè)單元格中設(shè)置圖像,文本和輔助文本。 - 2) 將寵物的數(shù)據(jù)應(yīng)用于配置,包括寵物的圖像。
- 3) 設(shè)置圖像的尺寸。
- 4) 將
configuration
應(yīng)用于單元的contentConfiguration
。
構(gòu)建并運(yùn)行。
突然,寵物看起來(lái)更可愛(ài)了。 您是否有動(dòng)機(jī)領(lǐng)養(yǎng)個(gè)?
Adopting a Pet
最后,您領(lǐng)養(yǎng)一只寵物。 Diego
正在等你接他!
首先,您將學(xué)習(xí)如何為單元?jiǎng)?chuàng)建和應(yīng)用后臺(tái)配置。 養(yǎng)有寵物的牢Cells
將具有背景色。 您將使用iOS 14
中引入的UIBackgroundConfiguration
,它是現(xiàn)代單元配置的一部分。
入門項(xiàng)目已經(jīng)具有存儲(chǔ)被收養(yǎng)寵物的屬性: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
}
要為單元格提供彩色背景,請(qǐng)執(zhí)行以下操作:
- 1) 檢查寵物是否被收養(yǎng)。 只有收養(yǎng)的寵物才會(huì)有彩色背景。
- 2) 創(chuàng)建一個(gè)
UIBackgroundConfiguration
,為listPlainCell
配置默認(rèn)屬性。 將其分配給backgroundConfig
。 - 3) 接下來(lái),根據(jù)您的喜好修改
backgroundConfig
。 - 4) 將
backgroundConfig
分配給cell.backgroundConfiguration
。
您尚無(wú)法測(cè)試。 您需要先養(yǎng)寵物。
入門項(xiàng)目有一個(gè)PetDetailViewController
。 該視圖控制器具有Adopt
按鈕。 但是,如何導(dǎo)航到PetDetailViewController
?
您向?qū)櫸?code>cell添加一個(gè)disclosure indicator
。 在petCellRegistration()
和cell.contentConfiguration = configuration
下,添加:
cell.accessories = [.disclosureIndicator()]
在這里設(shè)置單元格的disclosure indicator
。
現(xiàn)在,當(dāng)您點(diǎn)擊寵物單元格時(shí),您需要導(dǎo)航到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))
當(dāng)您點(diǎn)擊pet cell
時(shí),將調(diào)用collectionView(_:didSelectItemAt :)
。 在此代碼中,您:
- 1) 檢查所選
indexPath
處的item
是否存在。 - 2) 安全解包
pet
。 - 3) 然后,將
PetDetailViewController
推入導(dǎo)航堆棧。pushDetailForPet()
是入門項(xiàng)目的一部分。
構(gòu)建并運(yùn)行。 尋找Diego
,然后點(diǎn)按單元格。
這是你的朋友Diego
! 點(diǎn)擊Adopt
按鈕。
您已采用Diego
,然后導(dǎo)航回Pet Explorer
。 您會(huì)期望Diego
的單元格具有藍(lán)色背景,但背景并非如此。 發(fā)生了什么?
數(shù)據(jù)源尚未更新。 您現(xiàn)在就要做。
將以下方法添加到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
應(yīng)用于dataSource
。
現(xiàn)在確保收養(yǎng)寵物時(shí)調(diào)用updateDataSource(for :)
。
在petDetailViewController(_:didAdoptPet :)
中,添加:
// 1
adoptions.insert(pet)
// 2
updateDataSource(for: pet)
用戶收養(yǎng)寵物時(shí),將調(diào)用此代碼。 在這里:
- 1) 將收養(yǎng)的
pet
插入adoptions
。 - 2) 調(diào)用
updateDataSource(for :)
。 這是您剛創(chuàng)建的方法。
構(gòu)建并運(yùn)行。 點(diǎn)擊Diego
。 然后,在詳細(xì)信息屏幕上,點(diǎn)擊Adopt
。 向后瀏覽后,您會(huì)看到以下屏幕。
Diego
有藍(lán)色背景。 他現(xiàn)在是你的。
What is a Section Snapshot?
section snapshot
將UICollectionView
中單個(gè)section
的數(shù)據(jù)封裝起來(lái)。 這有兩個(gè)重要的好處:
- 1)
Section snapshot
使model hierarchical data
成為可能。 在實(shí)施帶有寵物類別的列表時(shí),您已經(jīng)應(yīng)用了此功能。 - 2)
UICollectionView
數(shù)據(jù)源每個(gè)section
可以有一個(gè)snapshot
,而不是整個(gè)collection view
的單個(gè)快照。 這使您可以將多個(gè)sections
添加到collection view
中,其中每個(gè)部分可以具有不同的布局和行為。
您將為收養(yǎng)的寵物添加一個(gè)section
,以了解其工作原理。
Adding a Section for Adopted Pets
您想在collectionView
中的單獨(dú)section
中創(chuàng)建收養(yǎng)的寵物列表,其中包含您之前創(chuàng)建的寵物類別的可擴(kuò)展列表下方。
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) 創(chuàng)建一個(gè)返回
NSCollectionLayoutSection
的閉包。 現(xiàn)在您有多個(gè)sections
,并且此閉包可以根據(jù)sectionIndex
分別返回每個(gè)section
的布局。 在這種情況下,各section
的布局相同,因此您無(wú)需使用sectionIndex
。您將閉包分配給provider
。layoutEnv
提供有關(guān)布局環(huán)境的信息。 - 2) 為具有
.grouped
外觀的列表創(chuàng)建配置。 - 3) 返回具有給定
configuration
的section
的NSCollectionLayoutSection.list
。 - 4) 使用
provider
作為sectionProvider
創(chuàng)建UICollectionViewCompositionalLayout
。 您將布局分配給collectionView.collectionViewLayout
。
接下來(lái),您將配置展示。
2. Configuring the Presentation
將以下方法添加到第一個(gè)PetExplorerViewController
擴(kuò)展塊中:
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()]
}
}
此代碼將應(yīng)用于.adoptedPets
中的單元格。 它對(duì)您應(yīng)該看起來(lái)很熟悉。 與您在Configuring the Cells
中添加的petCellRegistration()
類似。 現(xiàn)在,您將配置數(shù)據(jù)。
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)
}
使用此代碼,可以使數(shù)據(jù)源返回的單元格依賴于該section
。 在這里:
- 1) 安全地解包
section
。 - 2) 返回
.availablePets
的petCellRegistration()
- 3) 返回
.adoptedPets
的enabledPetCellRegistration()
是時(shí)候?qū)⑦@些sections
添加到數(shù)據(jù)源中了。
在applyInitialSnapshots()
中,在方法的開(kāi)頭插入以下代碼:
// 1
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
// 2
snapshot.appendSections(Section.allCases)
// 3
dataSource.apply(snapshot, animatingDifferences: false)
在此代碼中,您:
- 1) 創(chuàng)建一個(gè)新的
snapshot
。 - 2) 將所有
sections
追加到snapshot
。 - 3) 將
snapshot
應(yīng)用于dataSource
。
構(gòu)建并運(yùn)行。 領(lǐng)養(yǎng)Diego
。
Diego
的背景是藍(lán)色,所以您知道收養(yǎng)成功了。 但是您添加的section
在哪里?
該section
在那里,但為空。 您已將Diego
添加到了收養(yǎng)的寵物中,但尚未將他插入數(shù)據(jù)源中。 這就是您現(xiàn)在要做的。
在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) 從數(shù)據(jù)源中檢索
.adoptedPets
的snapshot
。 您將其分配給adoptedPetsSnapshot
。 - 2) 為收養(yǎng)的
pet
創(chuàng)建一個(gè)新Item
,并將其分配給newItem
。 - 3) 將
newItem
追加到adoptedPetsSnapshot
。 - 4) 您將修改后的
acceptedPetsSnapshot
應(yīng)用于dataSource
的.adoptedPets
。
構(gòu)建并運(yùn)行。
起作用了! Diego
在收養(yǎng)寵物部分。
您已經(jīng)了解了iOS 14
中UICollectionView
的許多改進(jìn),其中包括:
- 1) 使用
UICollectionLayoutListConfiguration
創(chuàng)建可擴(kuò)展list
。 - 2) 使用
Modern Cell Configuration
來(lái)配置UICollectionView
單元。 - 3) 使用
Section Snapshots
可將多個(gè)sections
添加到UICollectionView
。
而您只接觸了表面。 有關(guān)更多詳細(xì)信息,請(qǐng)查看WWDC 2020中的Advances in UICollectionView。
后記
本篇主要講述了基于
iOS14
的UICollectionView List
的創(chuàng)建,感興趣的給個(gè)贊或者關(guān)注~~~