UIKit框架(五十一) —— 基于iOS14的UICollectionView List的創(chuàng)建(一)

版本記錄

版本號 時間
V1.0 2021.01.25 星期一

前言

iOS中有關(guān)視圖控件用戶能看到的都在UIKit框架里面,用戶交互也是通過UIKit進(jìn)行的棍弄。感興趣的參考上面幾篇文章望薄。
1. UIKit框架(一) —— UIKit動力學(xué)和移動效果(一)
2. UIKit框架(二) —— UIKit動力學(xué)和移動效果(二)
3. UIKit框架(三) —— UICollectionViewCell的擴(kuò)張效果的實(shí)現(xiàn)(一)
4. UIKit框架(四) —— UICollectionViewCell的擴(kuò)張效果的實(shí)現(xiàn)(二)
5. UIKit框架(五) —— 自定義控件:可重復(fù)使用的滑塊(一)
6. UIKit框架(六) —— 自定義控件:可重復(fù)使用的滑塊(二)
7. UIKit框架(七) —— 動態(tài)尺寸UITableViewCell的實(shí)現(xiàn)(一)
8. UIKit框架(八) —— 動態(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布局的簡單示例(一)
16. UIKit框架(十六) —— 基于自定義UICollectionViewLayout布局的簡單示例(二)
17. UIKit框架(十七) —— 基于自定義UICollectionViewLayout布局的簡單示例(三)
18. UIKit框架(十八) —— 基于CALayer屬性的一種3D邊欄動畫的實(shí)現(xiàn)(一)
19. UIKit框架(十九) —— 基于CALayer屬性的一種3D邊欄動畫的實(shí)現(xiàn)(二)
20. UIKit框架(二十) —— 基于UILabel跑馬燈類似效果的實(shí)現(xiàn)(一)
21. UIKit框架(二十一) —— UIStackView的使用(一)
22. UIKit框架(二十二) —— 基于UIPresentationController的自定義viewController的轉(zhuǎn)場和展示(一)
23. UIKit框架(二十三) —— 基于UIPresentationController的自定義viewController的轉(zhuǎn)場和展示(二)
24. UIKit框架(二十四) —— 基于UICollectionViews和Drag-Drop在兩個APP間的使用示例 (一)
25. UIKit框架(二十五) —— 基于UICollectionViews和Drag-Drop在兩個APP間的使用示例 (二)
26. UIKit框架(二十六) —— UICollectionView的自定義布局 (一)
27. UIKit框架(二十七) —— UICollectionView的自定義布局 (二)
28. UIKit框架(二十八) —— 一個UISplitViewController的簡單實(shí)用示例 (一)
29. UIKit框架(二十九) —— 一個UISplitViewController的簡單實(shí)用示例 (二)
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轉(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的簡單實(shí)用示例(一)
44. UIKit框架(四十四) —— CALayer的簡單實(shí)用示例(二)
45. UIKit框架(四十五) —— 支持DarkMode的簡單示例(一)
46. UIKit框架(四十六) —— 支持DarkMode的簡單示例(二)
47. UIKit框架(四十七) —— 自定義Calendar Control的簡單示例(一)
48. UIKit框架(四十八) —— 自定義Calendar Control的簡單示例(二)
49. UIKit框架(四十九) —— UIVisualEffectView原理和簡單使用(一)
50. UIKit框架(五十) —— UIVisualEffectView原理和簡單使用(二)

開始

首先看下主要內(nèi)容:

在本教程中痕支,您將學(xué)習(xí)如何在單個collection view中創(chuàng)建列表,使用現(xiàn)代單元配置(cell configuration)以及配置多個section snapshot蛮原。內(nèi)容來自翻譯卧须。

接著看下寫作環(huán)境:

Swift 5, iOS 14, Xcode 12

下面就一起來看正文啦。

在iOS 14中儒陨,Apple為UICollectionView引入了新特性花嘶。Lists使您可以在UICollectionView中包括類似UITableView的部分。現(xiàn)代單元配置Modern Cell Configuration使注冊和配置collection view cell更加容易蹦漠。并且,Section Snapshots允許UICollectionView中包含多個section隘击,其中每個section可以具有不同的布局埋同。

在本教程中,您將學(xué)習(xí)如何:

  • 使用UICollectionLayoutListConfiguration創(chuàng)建一個可擴(kuò)展列表list哟冬。
  • 使用Modern Cell Configuration來配置UICollectionView cell
  • 使用Section Snapshots可將多個節(jié)(section)添加到UICollectionView错敢。

注意:本教程假定您熟悉Apple在iOS 13中引入的UICollectionViewDiffableDataSourceUICollectionViewCompositionalLayout。如果您以前從未使用過它們咽块,請查看Collection View and Diffable Data Source and Modern Collection Views with Compositional Layouts

事不宜遲亭罪,該開始了!

Xcode中打開啟動項(xiàng)目箩祥。構(gòu)建并運(yùn)行。

您會看到一個空的Pet Explorer屏幕盲泛。 它是Get a Pet應(yīng)用程序的一部分,該應(yīng)用程序顯示可供收養(yǎng)的寵物村视。 您將基于此應(yīng)用程序構(gòu)建。 在最終版本中杠氢,您可以瀏覽寵物類別并選擇一個寵物以查看其詳細(xì)信息。 然后因悲,當(dāng)您找到喜歡的寵物時晃琳,可以點(diǎn)擊Adopt以領(lǐng)養(yǎng)該寵物。

完整的應(yīng)用程序的Pet Explorer屏幕顯示可用和已收養(yǎng)的寵物:

注意可愛的狗Diego誊涯。完成本教程后,您將成為該虛擬小狗的驕傲擁有者取逾。

打開Xcode。瀏覽項(xiàng)目晴埂。當(dāng)應(yīng)用啟動時,它使用PetExplorerViewController作為根視圖控制器琅锻,將導(dǎo)航控制器設(shè)置為初始視圖控制器。打開Main.storyboard選中該設(shè)置。

打開PetExplorerViewController.swift以瀏覽此文件郁油。 PetExplorerViewControllercollectionView為空。稍后案站,您將使用代表寵物和寵物類別的列表項(xiàng)來填充它蟆盐。

Pet.swift具有與寵物有關(guān)的所有數(shù)據(jù)。

DataSource類型別名是為了方便起見痹愚。稍后在配置UICollectionView數(shù)據(jù)源時將使用它。

枚舉Section代表.availablePets.adoptedPetsUICollectionView部分动壤。

最后,在PetExplorerViewController擴(kuò)展程序中肩碟,您會找到pushDetailForPet(_:withAdoptionStatus :)。當(dāng)用戶選擇一個項(xiàng)目時,此方法將顯示PetDetailViewController吨拍。

打開PetDetailViewController.swift伊滋。這是一個簡單的視圖控制器類笑旺,可顯示寵物的圖像,姓名和出生年月乌妙。

現(xiàn)在,您已經(jīng)探索了應(yīng)用程序的結(jié)構(gòu)荠察,接下來是時候了解UICollectionView列表了。


What is a List?

listUICollectionView中的table view類似的視圖焕盟。您可以通過僅在少量代碼下將可配置的UICollectionViewCompositionalLayout應(yīng)用于UICollectionView的一section來創(chuàng)建列表。

您可以配置列表以顯示分層數(shù)據(jù),并可以折疊和展開列表項(xiàng)或使其看起來類似于傳統(tǒng)的table view沃于。如果您需要在應(yīng)用程序中使用table view,則可以使用帶有UICollectionView API的列表咨演,也可以使用傳統(tǒng)的UITableView饵较。

在大多數(shù)情況下,列表list更易于創(chuàng)建和配置打洼。

現(xiàn)在該創(chuàng)建您的第一個列表list了。


Creating a List

您將創(chuàng)建一個顯示寵物類別的平面列表僻弹。這將是您不使用UITableView的第一個table view。對于平面列表,與UITableView相比蚣抗,UICollectionView list的優(yōu)勢可能不會立即顯現(xiàn)翰铡。稍后,當(dāng)您使列表可擴(kuò)展時迷捧,您會發(fā)現(xiàn)使用UICollectionView list的真正好處。

注意:UICollectionView體系結(jié)構(gòu)在布局膛堤,表示形式和數(shù)據(jù)之間有清晰的分隔绿渣。本教程的示例代碼遵循這種模式。每次向Get a Pet添加新功能時誉帅,您都會添加一段代碼,首先用于布局郭膛,然后用于演示则剃,最后用于數(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,而無需了解UICollectionViewCompositionalLayout API的詳細(xì)知識敷存。 您可以使用UICollectionLayoutListConfiguration配置列表的外觀觅闽,顏色,分隔符孕锄,頁眉和頁腳。

現(xiàn)在該將其應(yīng)用于您的代碼了:

打開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现使。 這為您提供了一個看起來像table view的布局配置顽冶。
  • 2) 接下來,您將創(chuàng)建一個具有list sectionUICollectionViewCompositionalLayout间景,該節(jié)section將使用該configuration十拣。 您需要將此布局應(yīng)用于collectionView

如您所見,整個布局配置只有兩行代碼铛只。

通過添加以下內(nèi)容在viewDidLoad()的末尾調(diào)用此方法:

configureLayout()

2. Configuring the Presentation

現(xiàn)在是時候?yàn)榱斜?code>list創(chuàng)建一個collection view cell了非竿。 單元格顯示寵物類別承匣。 您將了解注冊單元格的新方法。

在第一個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)建單元格注冊。 這是注冊collection view cell的現(xiàn)代方法。
  • 2) 您創(chuàng)建單元格注冊枫慷,并傳入一個閉包來配置單元格。 當(dāng)cell需要渲染時調(diào)用閉包顿颅。
  • 3) 然后配置cell斩跌。 寵物類別在item.title中可用柬批。 如果您不了解正在發(fā)生的事情众旗,請不要擔(dān)心岁忘。 本教程的整個章節(jié)都涉及現(xiàn)代單元配置(modern cell configuration)燃观。

在配置數(shù)據(jù)源時未舟,您將調(diào)用categoryCellregistration()术瓮。

3. Configuring the Data

您已為collection view配置了布局和單元格雕擂。 現(xiàn)在冰更,您需要一種機(jī)制,可以基于collection view的基礎(chǔ)數(shù)據(jù)創(chuàng)建這些單元格归斤。 這就是數(shù)據(jù)源的來源酝碳。

將以下方法添加到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)建并返回一個DataSource衡瓶,傳入collectionView和一個閉包捂齐,該閉包將UICollectionViewCell提供給數(shù)據(jù)源。
  • 2) 在閉包內(nèi)部贵少,您要求collectionView使UICollectionViewCelldequeue。 然后,您將單元格注冊作為參數(shù)傳遞序目,因此collectionView將知道其必須出隊(duì)的單元格類型稽揭。 您剛才創(chuàng)建的categoryCellregistration()包含單元配置的邏輯沮稚。

將以下屬性添加到PetExplorerViewController

lazy var dataSource = makeDataSource()

由于您在聲明中使用了lazy定拟,因此在第一次需要collectionView時會為其創(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ù)源來更新list的內(nèi)容罪针。 Apple在iOS 13中引入了diffable data source。該代碼尚無任何新的iOS 14功能。 當(dāng)您使列表可擴(kuò)展并將section snapshots添加到列表時瞒大,情況將會改變退渗。

使用applyInitialSnapshots()可以:

  • 1) 創(chuàng)建一個categorySnapshot,其中包含寵物類別名稱。
  • 2) 然后為每個類別category創(chuàng)建一個項(xiàng)目Item并將其添加到categories中。
  • 3) 將.availablePets附加到categorySnapshot匈辱。
  • 4) 然后將類別categories中的項(xiàng)目附加到categorySnapshot.availablePets畸裳。
  • 5) 將categorySnapshot應(yīng)用于dataSource

您已經(jīng)添加了一個部分section膨处,并指明了屬于該部分的所有元素舟肉。

現(xiàn)在围苫,在viewDidLoad()的末尾添加對applyInitialSnapshots()的調(diào)用:

applyInitialSnapshots()

構(gòu)建并運(yùn)行

恭喜你饱亿! 這是您的第一個帶有列表的UICollectionView

列表list支持與UITableView樣式匹配的外觀:.plain配猫,.grouped.insetGrouped幅恋。 您創(chuàng)建的列表具有.grouped外觀。

iOS 14具有用于將列表顯示為邊欄的新外觀:.sidebar.sidebarPlain泵肄。 它們通常在拆分視圖(split view)中用作主視圖捆交。

現(xiàn)在,您可以擴(kuò)展列表腐巢。


Making the List Expandable

現(xiàn)在是時候?qū)?code>pets添加到類別中了品追。

在這里,您將發(fā)現(xiàn)UICollectionView列表的強(qiáng)大功能冯丙。 使用UITableView肉瓦,您將不得不處理category cells and pet cell上的點(diǎn)擊tap,保持單元格的可見狀態(tài)和展開狀態(tài),并編寫顯示或隱藏寵物單元格的代碼泞莉。

使用UICollectionView列表哪雕,您只需要提供類別和寵物的分層數(shù)據(jù)結(jié)構(gòu)。 該列表將處理其余的內(nèi)容鲫趁。 您很快就會發(fā)現(xiàn)斯嚎,僅需幾行代碼就可以實(shí)現(xiàn)很多目標(biāo)。

Pet.swift包含所有寵物及其所屬類別的數(shù)據(jù)饮寞。 無需更改布局中的任何內(nèi)容孝扛,因此您將從展示開始。

1. Configuring the Presentation

之前幽崩,您為pet category創(chuàng)建了一個單元格。 您了解了注冊單元格的新方法寞钥。 在這里慌申,您將執(zhí)行相同的操作,這次將為寵物創(chuàng)建一個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)建一個單元注冊并使用現(xiàn)代單元配置來配置該單元柒爵。 在這里,您使用defaultContentConfiguration()赚爵,然后將寵物名稱分配為要顯示的文本棉胀。

在配置數(shù)據(jù)源時,您將調(diào)用petCellRegistration()冀膝。

接下來唁奢,您可以通過向類別單元格添加outline disclosure accessory來擴(kuò)展列表。 這表明一個項(xiàng)目可以展開和折疊窝剖。 當(dāng)您點(diǎn)擊一個類別時麻掸,列表將展開并顯示該類別的寵物。

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)用于disclosureAccessoryoptions疙描。 您使用.header樣式使單元格可擴(kuò)展诚隙。
  • 2) 然后,使用配置的options創(chuàng)建一個disclosureAccessory淫痰。
  • 3) 將accessory應(yīng)用于cell最楷。 單元cell可以具有多個附件,因此您可以在一個數(shù)組中添加discoveryAccessory

構(gòu)建并運(yùn)行籽孙。

outline disclosure是可見的烈评,但是當(dāng)您點(diǎn)擊一個單元格時,什么也不會發(fā)生犯建。 為什么讲冠? 您尚未將寵物添加到其類別中。 接下來适瓦,您將要執(zhí)行此操作竿开。

2. Configuring the Data

接下來,您將學(xué)習(xí)如何將分層數(shù)據(jù)添加到列表中玻熙。 完成后否彩,您會看到列表自動支持折疊和展開單元格。

現(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)
}

一個item可以代表一個類別或一個寵物枚尼。 這取決于pet的值贴浙。 在此代碼中,collectionViewdequeue

  • 1) 如果item.pet不為nil署恍,則為petcell崎溃。
  • 2) 如果item.petnil,則為categorycell盯质。

您已配置了展示寵物所需的一切袁串,但尚未添加任何寵物。 為此唤殴,您必須更新數(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)系,請執(zhí)行以下操作:

  • 1) 創(chuàng)建類型為NSDiffableDataSourceSectionSnapshotcategorySnapshot朵逝。 這是一個section snapshot蔚袍。 使用section snapshot,可以用分層結(jié)構(gòu)表示數(shù)據(jù)配名,例如帶有可擴(kuò)展項(xiàng)的輪廓啤咽。目前,這是有關(guān)section snapshot的全部知識渠脉。 在本教程的后面宇整,您將了解有關(guān)section snapshot的更多信息。
  • 2) 然后芋膘,遍歷Pet.Category.allCases中的類別鳞青。 在循環(huán)中霸饲,將寵物添加到其類別中。
  • 3) 創(chuàng)建一個categoryItem臂拓。
  • 4) 將categoryItem追加到categorySnapshot厚脉。
  • 5) 然后,創(chuàng)建一個數(shù)組petItems胶惰,其中包含屬于當(dāng)前類別category的所有寵物傻工。
  • 6) 通過將petItems附加到當(dāng)前categoryItem來創(chuàng)建類別和寵物之間的層次關(guān)系。
  • 7) 將categorySnapshot應(yīng)用于dataSource.availablePets孵滞。

構(gòu)建并運(yùn)行中捆。

點(diǎn)擊一個類別。列表將展開并顯示寵物名稱坊饶。很好泄伪!

現(xiàn)在是時候讓cell看起來更好一點(diǎn)了。


What is Modern Cell Configuration?

如果您使用過UITableViewUICollectionView幼东,則習(xí)慣于通過直接設(shè)置單元格的屬性來對其進(jìn)行配置臂容。在iOS 14中,單元配置可以完全與單元本身分離根蟹。

您創(chuàng)建類型為UIContentConfigurationcell content configuration。然后糟秘,根據(jù)需要設(shè)置此內(nèi)容配置的屬性简逮。同樣,您可以創(chuàng)建類型為UIBackgroundConfigurationcell background configuration尿赚。

結(jié)果是可重用的配置散庶,您可以將其應(yīng)用于您喜歡的任何cell

現(xiàn)在該看看它是如何工作的凌净!


Configuring the Cells

您剛剛學(xué)習(xí)了modern cell configuration的理論”辏現(xiàn)在,您將通過添加代碼來更新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的配置贬养。使用此配置,您將在一個單元格中設(shè)置圖像琴庵,文本和輔助文本误算。
  • 2) 將寵物的數(shù)據(jù)應(yīng)用于配置仰美,包括寵物的圖像。
  • 3) 設(shè)置圖像的尺寸儿礼。
  • 4) 將configuration應(yīng)用于單元的contentConfiguration咖杂。

構(gòu)建并運(yùn)行。

突然蜘犁,寵物看起來更可愛了翰苫。 您是否有動機(jī)領(lǐng)養(yǎng)個?


Adopting a Pet

最后这橙,您領(lǐng)養(yǎng)一只寵物奏窑。 Diego正在等你接他!

首先屈扎,您將學(xué)習(xí)如何為單元創(chuàng)建和應(yīng)用后臺配置埃唯。 養(yǎng)有寵物的牢Cells將具有背景色。 您將使用iOS 14中引入的UIBackgroundConfiguration鹰晨,它是現(xiàn)代單元配置的一部分墨叛。

入門項(xiàng)目已經(jīng)具有存儲被收養(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
}

要為單元格提供彩色背景漠趁,請執(zhí)行以下操作:

  • 1) 檢查寵物是否被收養(yǎng)。 只有收養(yǎng)的寵物才會有彩色背景忍疾。
  • 2) 創(chuàng)建一個UIBackgroundConfiguration闯传,為listPlainCell配置默認(rèn)屬性。 將其分配給backgroundConfig卤妒。
  • 3) 接下來甥绿,根據(jù)您的喜好修改backgroundConfig
  • 4) 將backgroundConfig分配給cell.backgroundConfiguration则披。

您尚無法測試共缕。 您需要先養(yǎng)寵物。

入門項(xiàng)目有一個PetDetailViewController士复。 該視圖控制器具有Adopt按鈕图谷。 但是,如何導(dǎo)航到PetDetailViewController判没?

您向?qū)櫸?code>cell添加一個disclosure indicator蜓萄。 在petCellRegistration()cell.contentConfiguration = configuration下,添加:

cell.accessories = [.disclosureIndicator()]

在這里設(shè)置單元格的disclosure indicator澄峰。

現(xiàn)在嫉沽,當(dāng)您點(diǎn)擊寵物單元格時,您需要導(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時绸硕,將調(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。 您會期望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) 查找代表petitem并將其分配給petItem购对。
  • 3) 在snapshot中重新加載petItem
  • 4) 然后將更新的snapshot應(yīng)用于dataSource陶因。

現(xiàn)在確保收養(yǎng)寵物時調(diào)用updateDataSource(for :)骡苞。

petDetailViewController(_:didAdoptPet :)中楷扬,添加:

// 1
adoptions.insert(pet)
// 2
updateDataSource(for: pet)

用戶收養(yǎng)寵物時,將調(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酥宴。 向后瀏覽后搔弄,您會看到以下屏幕。

Diego有藍(lán)色背景顾犹。 他現(xiàn)在是你的炫刷。


What is a Section Snapshot?

section snapshotUICollectionView中單個section的數(shù)據(jù)封裝起來。 這有兩個重要的好處:

  • 1) Section snapshot使model hierarchical data成為可能浑玛。 在實(shí)施帶有寵物類別的列表時,您已經(jīng)應(yīng)用了此功能极阅。
  • 2) UICollectionView數(shù)據(jù)源每個section可以有一個snapshot,而不是整個collection view的單個快照筋搏。 這使您可以將多個sections添加到collection view中厕隧,其中每個部分可以具有不同的布局和行為。

您將為收養(yǎng)的寵物添加一個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)建一個返回NSCollectionLayoutSection的閉包。 現(xiàn)在您有多個sections侄非,并且此閉包可以根據(jù)sectionIndex分別返回每個section的布局流译。 在這種情況下,各section的布局相同福澡,因此您無需使用sectionIndex。您將閉包分配給provider除秀。 layoutEnv提供有關(guān)布局環(huán)境的信息算利。
  • 2) 為具有.grouped外觀的列表創(chuàng)建配置。
  • 3) 返回具有給定configurationsectionNSCollectionLayoutSection.list效拭。
  • 4) 使用provider作為sectionProvider創(chuàng)建UICollectionViewCompositionalLayout。 您將布局分配給collectionView.collectionViewLayout慕的。

接下來挤渔,您將配置展示。

2. Configuring the Presentation

將以下方法添加到第一個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中的單元格。 它對您應(yīng)該看起來很熟悉俯邓。 與您在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) 返回.availablePetspetCellRegistration()
  • 3) 返回.adoptedPetsenabledPetCellRegistration()

是時候?qū)⑦@些sections添加到數(shù)據(jù)源中了钳垮。

applyInitialSnapshots()中,在方法的開頭插入以下代碼:

// 1
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
// 2
snapshot.appendSections(Section.allCases)
// 3
dataSource.apply(snapshot, animatingDifferences: false)

在此代碼中歧焦,您:

  • 1) 創(chuàng)建一個新的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ù)源中檢索.adoptedPetssnapshot胸囱。 您將其分配給adoptedPetsSnapshot妹卿。
  • 2) 為收養(yǎng)的pet創(chuàng)建一個新Item,并將其分配給newItem箕宙。
  • 3) 將newItem追加到adoptedPetsSnapshot铺纽。
  • 4) 您將修改后的acceptedPetsSnapshot應(yīng)用于dataSource.adoptedPets沥阳。

構(gòu)建并運(yùn)行。

起作用了其馏! Diego在收養(yǎng)寵物部分。

您已經(jīng)了解了iOS 14UICollectionView的許多改進(jìn)叛复,其中包括:

  • 1) 使用UICollectionLayoutListConfiguration創(chuàng)建可擴(kuò)展list
  • 2) 使用Modern Cell Configuration來配置UICollectionView單元咖耘。
  • 3) 使用Section Snapshots可將多個sections添加到UICollectionView撬码。

而您只接觸了表面。 有關(guān)更多詳細(xì)信息夫否,請查看WWDC 2020中的Advances in UICollectionView叫胁。

后記

本篇主要講述了基于iOS14UICollectionView List的創(chuàng)建,感興趣的給個贊或者關(guān)注~~~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子堰酿,更是在濱河造成了極大的恐慌张足,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件哼绑,死亡現(xiàn)場離奇詭異碉咆,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)疫铜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門壳咕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來席揽,“玉大人谓厘,你說我怎么就攤上這事∈翳耄” “怎么了住练?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長亏吝。 經(jīng)常有香客問我盏混,道長,這世上最難降的妖魔是什么止喷? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任混聊,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘沟于。我一直安慰自己植康,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布供璧。 她就那樣靜靜地躺著冻记,像睡著了一般。 火紅的嫁衣襯著肌膚如雪檩赢。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天偶房,我揣著相機(jī)與錄音军浆,去河邊找鬼。 笑死掰盘,一個胖子當(dāng)著我的面吹牛赞季,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播申钩,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼撒遣,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了义黎?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤泻云,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后宠纯,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年勃救,在試婚紗的時候發(fā)現(xiàn)自己被綠了治力。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡晕讲,死狀恐怖马澈,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情勤婚,我是刑警寧澤涤伐,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站凝果,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏型雳。R本人自食惡果不足惜掌动,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望柑晒。 院中可真熱鬧眷射,春花似錦佛掖、人聲如沸涌庭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽席镀。三九已至,卻和暖如春豪诲,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背服赎。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工交播, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人嚎尤。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓伍宦,卻偏偏與公主長得像,于是被迫代替她去往敵國和親次洼。 傳聞我的和親對象是個殘疾皇子卖毁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容