筆記:Develop in Swift Data Collections_Dynamic Data

呈現不同的樣式
· 學習目標
· 基礎概念
· 1.Adding a Search Controller
· 2.Handling Data Changes
2–1.比較performBatchUpdates、Diffable Data Sources
· 3.Diffable Data Sources(可差異化的數據源)
3–1.使用 UICollectionViewDiffableDataSource
3–2.實作
GitHub
· Lab - iTunes Search (Part 4)
· 1.Review Provided Refactoring
· 2.Add Diffable Data Source for Table View
2–1.引入 Diffable Data Source:
2–2.資料類型設定
2–3.設定 TableView 的 Data Source
2–4.設定和初始化資料來源
2–5.創建和應用SnapShot
2–6.整合到 App 中
· 3.Implement Collection View
3–1.設計Cell
3–2.建立Outlet和Layout設計
3–3.數據源和更新
3–4.設置數據源方法
3–5.程式碼重構
3–6.更新類別定義
3–7.測試
GitHub

學習目標

  • UICollectionViewDiffableDataSource
使用泛型的API,用於管理 CollectionView (如表格和網格)的數據。
它簡化了數據提供流程,自動加入像是數據變動時的動畫這類功能。
  • 實作UISearchController:
目的:為 App 添加數據篩選功能。
應用:讓用戶能夠搜尋特定項目或篩選出特定條件的數據。
  • 更新 CollectionView 數據
重點:學會在數據有變動時怎麼更新視圖。
方法:通常要觸發視圖的重新加載或更新。
  • 建立與運用可擴展數據源(Diffable Data Sources):
建立:設定一種可以處理數據變更(增加、刪除、更新)的新型數據源。
使用:用簡單的API來管理 CollectionView 的數據更新。
  • 利用可擴展數據源快照(snapshot)更新 CollectionView
快照(snapshot):是描述 CollectionView 目前狀態(所有項目和部分)的一種方式。
更新方法:透過應用「新快照」來反映數據變化,無需手動處理每一個細節。

基礎概念

  • Diffable data source(可差異化的數據源)
這是一種特殊的數據源,用於與表格視圖(TableView)或集合視圖(CollectionView)協同工作,
以簡單有效的方式管理視圖的數據和使用者介面。

1. 簡化資料管理:
- 這種數據源幫忙處理 TableView 或 CollectionView 的資料,更輕鬆地管理。

2. 自動比對差異:
- 隨著資料改變,它會自動找出舊資料和新資料的不同,更新視圖。

3. 保持介面整齊:
- 因為它會自己處理資料的變化,所以表格或集合視圖看起來總是整齊有序。

4. 提升效率:
- 這種方法比手動更新資料和介面更省事,節省了寫很多程式碼的麻煩。

5. 動態變化無壓力:
- 不管是加資料、刪資料,還是換順序,這個數據源都能應付自如,而且變化時還會有動畫效果。
  • Search controller
1. 標準搜尋介面:
- Search controller 讓使用者可以方便地輸入他們想找的內容。

2. 互動式搜尋框:
- 當使用者在搜尋框打字時,Search controller 會啟動,開始處理搜尋動作。

3. 呈現搜尋結果:
- Search controller 會和專門顯示「搜尋結果控制器」協同工作,確保搜尋結果能夠清楚地展示給使用者。

4. 開發上的便利:
- 這個控制器的設計讓開發者更容易在他們的 App 中加入搜尋功能,不需要從頭開始設計整個搜尋流程。
  • Snapshot(快照)
Snapshot就是在某一時刻,用來呈現 collection view 或 table view 裡面數據的狀態。

1.包含的數據:
- 快照包含想在視圖中展示的各個部分(sections)和項目(items),
並按照我們希望的順序來排列。

2. 配置顯示內容:
- 可以通過添加、刪除或移動這些部分和項目來設置想展示的內容。

3. 應用於可擴展數據源:
- 當配置好快照後,就會應用它到一個可擴展數據源(diffable data source)。
這個數據源會根據「當前數據狀態」和「新快照」之間的差異來進行更新。
  • UISearchController 和單獨的 SearchBar 在功能和用法上的主要差異
一般來說,如果需要更豐富的搜尋功能並且想要與導航結構緊密整合,則 UISearchController 是較好的選擇。
對於更簡單或特定的應用場景,單獨的 SearchBar 可能會更加方便和適合。

1.Adding a Search Controller

  • 利用現有的Navigation Stacks(導航堆疊)API,整合Search Controller。因為ViewController已經嵌入在一個NavigationController中,所以可以快速加入「搜尋欄界面」。
  • 建立Search Controller
使用 UISearchController() 初始化搜尋控制器。
如果搜尋結果要用相同的視圖顯示,就不需設置 searchResultsController。
是否需要根據「搜尋結果的布局」選擇是否設置searchResultsController
  • 設定 Search Controller
在 viewDidLoad() 加入幾行程式碼來配置Search Controller
  • 遵循 UISearchResultsUpdating 協定:
為了處理搜尋結果,需要符合 UISearchResultsUpdating 協定。
  • 更新搜尋結果的方法 (updateSearchResults):
當我們在搜尋欄打字時,會根據輸入的文字更新顯示的內容。
目前的情況是,即使加了搜尋欄,打字進去也不會改變畫面上的內容。
  • 搜尋功能實踐:
1. 複製一份原始資料,用來做為搜尋的結果。
2. 過濾這份複製資料,只留下符合搜尋條件的部分。
3. 更新顯示內容,只展示過濾後的結果。
  • 程式碼:
1. 增加一個新的變數 filteredItems,這是 items 的一份複製。
- filteredItems 用來儲存過濾後的結果,也會取代 items 成為集合視圖資料的基礎。

2. 把原本Data Source、Delegate Methods 中 items 的地方都改成顯示 filteredItems。
  • 實作 updateSearchResults
1. 檢查搜尋欄有沒有輸入文字,而且這些文字不能是空的。
2. 如果有輸入「搜尋條件」,就用 「filter 方法」來找出符合的資料。
3. 使用 localizedCaseInsensitiveContains 來比對文字,不分大小寫。
4. 如果沒有輸入搜尋條件,filteredItems 就會重設為全部的 items。

EX:
- 搜尋字串 “茶” 將匹配到 “伯爵茶拿鐵”,“蜂蜜柚子茶”等。
- 如果沒有搜尋文字,則將 filteredItems 重設為整個 items,以便顯示所有資料集。
  • 測試

2.Handling Data Changes

  • 改善使用者體驗:
原來情況:使用者搜尋時,資料會突然變更,顯得不夠平滑。
目標:希望能用流暢的動畫效果更新資料,提升使用者感受。
  • 實現平滑動畫:
1. iOS 提供特定 API,讓 collection view 和 table view 在更新資料時可以展現平滑動畫。

2. performBatchUpdates(_:completion:) :
- 這個方法可以透過插入、刪除、重新載入和移動 cells(以 IndexPath 標識)來更新資料。
  • 實作挑戰:
儘管 performBatchUpdates 很強大,但要計算出「資料集」之間的差異常常不容易。

EX: 如果資料有被移除、移動或新增,要自己寫出這些變化的程式碼既複雜又容易出錯。
  • 更簡便的方法:
為了解決這個問題,iOS 提供了一個更簡單、更少出錯的 API。
這個 API 讓處理複雜的資料更新變得簡單,減少手動計算錯誤的風險。

2–1.比較performBatchUpdates、Diffable Data Sources

EX:
- 對於需要高度定制化動畫或者特定的更新邏輯的應用,使用 performBatchUpdates 可能更合適。
- 而對於數據頻繁更新且結構較為複雜的應用,Diffable Data Sources 可能提供更好的解決方案。

3.Diffable Data Sources(可差異化的數據源)

  • 慨念
1. 當在 Collection View 或 Table View 中展示靜態資料時,傳統的DataSource方法已經足夠。

2. 若資料會隨著用戶瀏覽而變動,使用 Diffable Data Sources 會是更好的選擇。

3. 「Diffable」指這種資料來源能夠描述「兩組資料」間的差異並進行複雜操作。

3–1.使用 UICollectionViewDiffableDataSource

1. 這是一個實現 「UICollectionViewDataSource」 的具體類別。

2. 它提供了一個基於閉包的API來作為 CollectionView 的資料來源。

3. 使用時,需將資料封裝在 NSDiffableDataSourceSnapshot實例中,然後提供給
Diffable Data Sources 實例。

## 如果是 UITableView 的話可以使用 UITableViewDiffableDataSource

3–2.實作

  • 建立 Section 的 Enum:
1. 在 CollectionViewController 中,首先要定義 CollectionView 中會有哪些不同的Section。
這通常是通過建立一個 Enum 來實現的。

EX:
- 如果 CollectionView 只有一種類型的內容,可能只需要一個名為「main」的Section。

2. Enum 需要符合 Hashable 協議,這樣它就可以被用作識別不同 Section 的唯一標識。
  • 添加資料來源(dataSource)變數:
1. 在 CollectionViewController 中,宣告一個「資料來源」的變數。
- 這是 UICollectionViewDiffableDataSource 的實例,管理和提供資料給 CollectionView。

2. 這個資料來源需要知道 Secction 和 Item 的類型,使用之前定義的「區段列舉」和
項目類型(如String或自定義類型)來指定它。
  • 實作資料來源dataSource
1. UICollectionViewDiffableDataSource需要兩個參數類型:
- SectionIdentifierType(區段列舉)和ItemIdentifierType(項目的類型)。

2. 需要實作一個閉包來配置每一個Item的視圖(通常是一個cell)。
- 這個閉包類似於之前在 collectionView(_:cellForItemAt:)中所做的,但它還會
接收一個我們的Item類型的實例。

3. 在 CollectionView 的初始化方法中,或在它載入視圖時,創建
UICollectionViewDiffableDataSource的實例。
  • 建立和應用快照(Snapshot)
1. NSDiffableDataSourceSnapshot 是用來捕捉 dataSource 在「某一時刻」的狀態。
- 需要創建一個 Snapshot,並將資料(區段和項目)加入到這個快照中。

2. 建立 Snapshot ,並將其應用到 dataSource 上,以便自動刷新相關集合或表格視圖的內容。
  • 應用「新快照」以反映資料變更:
1. 每當資料發生變化時(例如,用戶透過搜索更改了顯示的資料),需要創建一個「新的快照」
並應用它來更新集合視圖。

2. 這允許 App 動態地更新介面,並能夠以平滑的動畫來呈現這些變化。
  • 測試

Lab — iTunes Search (Part 4)

  • 練習在 App 靈活地使用CollectionViewSearchControllerDiffable Data Sources
  • 重構的目的:
使用自定義 ContainerViewController:
這個重構版本使用自定義的ContainerViewController。這種控制器能夠在兩個視圖控制器的視圖之間進行切換。

1.Review Provided Refactoring

  • 控制器結構
NavigationController:
- 不再是單一的表格視圖控制器,其「根視圖控制器」透過嵌入連接到「表格視圖」和「集合視圖控制器」。

ContainerViewController:
- 位於中間的是 ContainerViewController ,包含了表格視圖和集合視圖。
  • 視圖屬性設定:
Outlet:當執行嵌入Outlet時,ContainerViewController 會設定其他兩個視圖控制器的視圖屬性。
  • 切換顯示方式:
底部有一個分段控制,用來切換兩個容器視圖的 isHidden 屬性,以此顯示表格或集合視圖。
  • 集中管理數據:
StoreItemContainerViewController
- 負責獲取數據並填充表格和集合視圖的數據源。
- 因為兩種視圖使用相同的數據,所以集中在一個視圖控制器中管理數據是合理的。
  • SearchController更新:
UISearchController 替代 UISearchBar:
- 現在使用 UISearchController 來替代原本的搜尋欄,配置為始終可見。
  • 改善任務管理:
任務變數保存:
- 用來抓取「搜尋項目」的任務現在被保存在變數中,可以在搜尋條件改變時取消。
  • 搜尋結果更新方法:
防止過度請求:
- 為了避免每次鍵盤輸入都發送請求,使用了一種叫做「去抖動」(debounce)的技巧,
在用戶停止輸入一段時間後才發送網路請求。

2.Add Diffable Data Source for Table View

  • StoreItemContainerViewController 設置異步任務處理

2–1.引入 Diffable Data Source:

  • 目前缺少表格視圖資料來源的實作,所以App目前還無法展示特別的功能。
  • 將會同時為 TableViewCollectionView 使用 Diffable Data Source

2–2.資料類型設定

  • StoreItem 的設定:
Diffable Data Source 
- 將使用 String 作為 SectionIdentifierType。
- 而 StoreItem 作為 ItemIdentifierType。
- 要使用 StoreItem,首先要讓它遵守 Hashable 協議。
  • StoreItem 的變化:
除了加入 Hashable,還增加了 trackId 和 collectionId 兩個屬性,以確保 Hashable 
的雜湊函數有獨特值。

2–3.設定 TableView 的 Data Source

在 StoreItemContainerViewController 中,增加新屬性以保存 TableView 的 Diffable Data Source。
我使用Enum Section來進行管理

2–4.設定和初始化資料來源

  • 配置 TableView 的 Data Source
1. 添加方法 configureTableViewDataSource(_ tableView: UITableView) 來創建數據源。
2. 使用 cellProvider 閉包來參照 cell 配置的實現。
  • 處理圖片加載
圖片加載的管理:
- 在配置方法中,對於每個 cell 加載圖片時,都會檢查並取消之前的圖片加載任務,
確保只加載最新的圖片。

2–5.創建和應用SnapShot

  • 快照的計算屬性:
新增計算屬性 itemsSnapshot
- 用於從 items 陣列創建 NSDiffableDataSourceSnapshot<Section, StoreItem>。
  • 應用 SnapShot
  - 在「搜索結果」變更時,應用這個快照,以獲得更佳的動畫效果。
- 在搜索條件為空或搜索出錯時,也要應用空快照來正確動畫地移除項目。

## 此外取消所有表格是途中未完成的圖片加載任務 ##

2–6.整合到 App 中

  • 呼叫配置方法:
在 prepare(for:sender:) 中,擷取 segue 的目標視圖控制器作為
StoreItemListTableViewController,並呼叫「資料來源」配置方法。
  • 測試:
表格視圖「搜索結果」變化時的平滑動畫效果。

3.Implement Collection View

  • 接下來是要處理 CollectionView 的單元格(cell)並使用組合式佈局來實現特定設計。

3–1.設計Cell

使用Stack View:
- 包括 ImageView 和 Lable。

子類別建立:
- 建立一個 UICollectionViewCell 的子類別, ItemCollectionViewCell。

cell 設置:
- 設定cell id 為 "ItemCollectionViewCell",並把它的類別指定為 ItemCollectionViewCell。

3–2.建立Outlet和Layout設計

ItemCollectionViewCell 設置 Outlet:
- UIImageView、UILabel。
  • 設置組合式佈局(CompositionalLayout)
設計中每行有三個項目,每個項目之間有一點空間。

3–3.數據源和更新

建立 CollectionView的 數據源:
- 像 TableView 所做的那樣,使用 itemsSnapshot 更新數據源。

添加 UICollectionViewDiffableDataSource 變數:
- 在 StoreItemContainerViewController 中設置,名稱為 collectionViewDataSource。

3–4.設置數據源方法

  1. 新方法建立:名為 configureCollectionViewDataSource(_:)
  2. 方法內容:從集合視圖而非表格視圖取得單元格。
創建 configureCollectionViewDataSource 方法:
- 方法內部將從 collectionView 取得cell 。

(與前面製作configureTableViewDataSource的方式一樣)

此外這邊做完,再設置 collectionViewDataSource動畫效果基本上就可以運行了。

3–5.程式碼重構

重構程式碼整理:
- 將 cell 配置程式碼整理成一個方法,讓兩種「數據源」都能調用。

使用協議:
- 建立一個 ItemDisplaying 協議,定義 ItemTableViewCell 和 ItemCollectionViewCell 共有的特性。

協議擴展:
- 在 ItemDisplaying 下加入擴展,讓所有實作此協議的類別都能使用。
擴展 ItemDisplaying

3–6.更新類別定義

1. 更新 ItemTableViewCell 和 ItemCollectionViewCell:
- 使它們遵循 ItemDisplaying 協議。

2. 刪除重複的程式碼:
- 在配置數據源的方法中調用新的 configure(for:storeItemController) 方法。

3. 更新 fetchMatchingItems() 方法:
- 同時調用 collectionViewDataSource.apply(_:animatingDifferences:completion:)
和原有的 tableViewDataSource。
遵循 ItemDisplaying
刪除重複的程式碼(重構)
更新 fetchMatchingItems() 實踐collectionView動畫效果

3–7.測試

  • 確保CollectionView可以正確展示項目,並與TableView的設計效果一致。

--

--

wei Tsao 學習紀錄
彼得潘的 Swift iOS / Flutter App 開發教室

Hi ! 我是wei , 先前未接觸過程式開發設計,想藉此來記錄自己的學習歷程,以利培養自己的程式邏輯 :)