IGListKit 教程

varentry: JournalEntry!letsolFormatter = SolFormatter()本文翻譯自 Ryan Nystrom, IGListKit Tutorial: Better UICollectionViews.

點(diǎn)擊閱讀原文


大部分App都以相同的方式開始: ?一些視圖, 幾個(gè)按鈕, 一到兩個(gè)列表. 但是隨著時(shí)間的推移,app也在慢慢成長(zhǎng), 很多特性在以各自的方式蛻變. ?在最后期限和產(chǎn)品經(jīng)理的壓力下, 也許你會(huì)對(duì)著你空白的數(shù)據(jù)源感到崩潰, 為了完成任務(wù), 最后留下大量的需要維護(hù)的控制器視圖. 快點(diǎn)開始采用IGListKit來減輕你的負(fù)擔(dān)吧

使用IGListKit 有效避免了使用UICollectionView的產(chǎn)生的大量試圖控制器,通過IGListKit創(chuàng)建列表,你可以使用解耦組件構(gòu)建APP,快速迭代,并支持任何類型的數(shù)據(jù)類型

在這個(gè)例子中, 我們將基于UICollectionView以使用IGListKit,然后擴(kuò)展該應(yīng)用程序最終完成

開始

您是美國(guó)航空航天局頂尖的軟件工程師之一,也是最新的火星任務(wù)。 該團(tuán)隊(duì)已經(jīng)構(gòu)建了Marslink應(yīng)用程序的第一個(gè)版本,您可以在這里下載。 項(xiàng)目下載后,打開Marslink.xcworkspace并運(yùn)行應(yīng)用程序。

目前為止,應(yīng)用程序只顯示一份宇航員日列表。

只船員需要新的功能,您都需要為此應(yīng)用添加功能。通過打開Marslink \ ViewControllerers \ ClassicFeedViewController.swift 熟悉項(xiàng)目。如果你曾經(jīng)使用過UICollectionView,你看起來非常標(biāo)準(zhǔn):

ClassicFeedViewController是一個(gè)UIViewController子類,在擴(kuò)展中實(shí)現(xiàn)UICollectionViewDataSource。

viewDidLoad()創(chuàng)建一個(gè)UICollectionView,注冊(cè)單元格,設(shè)置數(shù)據(jù)源,并將其添加到視圖層次結(jié)構(gòu)中。

loader.entries數(shù)組決定了section的數(shù)量,每個(gè)部分只有兩個(gè)單元格(一個(gè)用于日期,一個(gè)用于文本)。

日期單元格使用日志文本配置Sol日期和文本輸入單元格。

collectionView(_:layout:sizeForItemAt :)為日期單元格返回固定大小,并計(jì)算實(shí)際條目的文本大小。

一切似乎工作正常,但產(chǎn)品經(jīng)理帶有一些緊急的產(chǎn)品更新請(qǐng)求:

一名宇航員剛剛被困在火星上。 我們需要您添加一個(gè)天氣模塊和實(shí)時(shí)聊天。 你有48個(gè)小時(shí)。


來自JPL的工程師提供了一些模塊,但是他們需要你幫助把它們放在應(yīng)用程序中。

如果把宇航員帶回家的所有壓力都不夠,美國(guó)航空航天局的首席設(shè)計(jì)師只是向你們傳遞了每個(gè)子系統(tǒng)在應(yīng)用程序中的更新必須被動(dòng)畫化的要求,這意味著沒有reloadData()。

怎么才能把所有的組件加入程序中, 并且讓所有的轉(zhuǎn)化有動(dòng)畫呢?

介紹

雖然UICollectionView是一個(gè)非常強(qiáng)大的工具,具有強(qiáng)大的組件和庫。 保持?jǐn)?shù)據(jù)源和視圖同步是非常重要的,崩潰通常是由于斷開連接造成的。

IGListKit是由Instagram在Instagram創(chuàng)建的數(shù)據(jù)驅(qū)動(dòng)的UICollectionView框架。 通過這個(gè)框架,您可以提供一系列在UICollectionView中顯示的對(duì)象。 對(duì)于每種類型的對(duì)象,適配器創(chuàng)建一個(gè)名為section controller的內(nèi)容,該控件具有創(chuàng)建單元格的所有細(xì)節(jié)。


IGListKit會(huì)自動(dòng)分辨對(duì)象,并在UICollectionView上執(zhí)行動(dòng)畫批量更新以進(jìn)行任何更改。 這樣你就不必自己編寫批量更新,避免在這里列出的問題。

使用

IGListKit能很好的識(shí)別集合中的更改以及使用動(dòng)畫更新相應(yīng)行。它還可以輕松處理具有不同數(shù)據(jù)和UI的多個(gè)部分。考慮到這一點(diǎn),它是對(duì)新批次的要求的完美解決方案 - 現(xiàn)在開始實(shí)現(xiàn)他!

Marslink.xcworkspace仍然打開,右鍵單擊ViewControllers組,然后選擇新建文件...。添加一個(gè)新的Cocoa Touch Class,它將UIViewController命名為FeedViewController。

打開AppDelegate.swift并找到應(yīng)用程序(_:didFinishLaunchingWithOptions :)方法。找到將ClassicFeedViewController()推送到導(dǎo)航控制器并將其替換為的行:

nav.pushViewController(FeedViewController(),animated:false)

FeedViewController現(xiàn)在是根視圖控制器。您將繼續(xù)使用ClassicFeedViewController.swift進(jìn)行參考,但是FeedViewController將用于實(shí)現(xiàn)新的IGListKit強(qiáng)制集合視圖。

構(gòu)建并運(yùn)行并確保一個(gè)新的,空的視圖控制器顯示在屏幕上。

添加日記

打開FeedViewController.swift并將以下屬性添加到FeedViewController的頂部:

let loader = JournalEntryLoader()

JournalEntryLoader是一個(gè)將硬編碼的日記帳分錄加載到條目數(shù)組中的類。

將以下內(nèi)容添加到viewDidLoad()的底部:

loader.loadLatest()

loadLatest()是加載最新日記帳分錄的JournalEntryLoader方法。

添加 collectionView

現(xiàn)在是開始向視圖控制器添加一些IGListKit特定控件的時(shí)候了。在你做之前,你需要導(dǎo)入框架。在FeedViewController.swift頂部附近添加一個(gè)新的導(dǎo)入:

導(dǎo)入IGListKit

注意:本教程中的項(xiàng)目使用CocoaPods來管理依賴關(guān)系。 IGListKit是在Objective-C中編寫的,所以如果您手動(dòng)將其添加到項(xiàng)目中,則需要#import到您的橋接頭

將初始化的collectionView常量添加到FeedViewController的頂部:

let collectionView: IGListCollectionView = {

let view = IGListCollectionView(frame: CGRect.zero, collectionViewLayout: UICollectionViewFlowLayout())

view.backgroundColor = UIColor.black

return view

}()

IGListKit使用IGListCollectionView,它是UICollectionView的一個(gè)子類,它優(yōu)化一些功能并阻止其他功能。

這是從零大小的rect開始,因?yàn)橐晥D尚未創(chuàng)建。它正如ClassicFeedViewController一樣使用UICollectionViewFlowLayout。

背景顏色設(shè)置為NASA批準(zhǔn)的黑色。

將以下內(nèi)容添加到viewDidLoad()的底部:

view.addSubview(CollectionView)

這將新的collectionView添加到控制器的視圖。

viewDidLoad()下面添加以下內(nèi)容:

override func viewDidLayoutSubviews(){

super.viewDidLayoutSubviews()

collectionView.frame = view.bounds

}

viewDidLayoutSubviews()被覆蓋,設(shè)置collectionView框架以匹配視圖邊界。

IGListAdapter and data source

使用UICollectionView,需要采用UICollectionViewDataSource的某種數(shù)據(jù)源。 它的工作是返回部分和行數(shù)以及單個(gè)單元格。

在IGListKit中,可以使用所謂的IGListAdapter來控制集合視圖。 我們?nèi)匀恍枰螴GListAdapterDataSource協(xié)議的數(shù)據(jù)源,而不是返回計(jì)數(shù)和單元格,您可以提供數(shù)組和段控制器(稍后再提供)。

對(duì)于初學(xué)者,在FeedViewController.swift中,在FeedViewController的頂部添加以下內(nèi)容:

lazyvaradapter:IGListAdapter= {returnIGListAdapter(updater:IGListAdapterUpdater(), viewController:self, workingRangeSize:0)}()

這將為IGListAdapter創(chuàng)建一個(gè)延遲初始化的變量。 初始化器需要三個(gè)參數(shù):

updater是符合IGListUpdatingDelegate的對(duì)象,它處理行和部分更新。 IGListAdapterUpdater是適合使用的默認(rèn)實(shí)現(xiàn)。

viewController是容納適配器的UIViewController。 此視圖控制器稍后用于導(dǎo)航到其他視圖控制器。

workingRangeSize是工作范圍的大小,它允許您為可見框架之外的部分準(zhǔn)備內(nèi)容。

注意:工作范圍是本教程未涉及的更高級(jí)的主題。 但是,IGListKit的備份還有很多文檔甚至是一個(gè)示例應(yīng)用程序!

將以下內(nèi)容添加到viewDidLoad()的底部:

adapter.collectionView = collectionView

adapter.dataSource =self

這是將collectionView連接到適配器。 它還將self設(shè)置為適配器的dataSource,這會(huì)導(dǎo)致編譯器錯(cuò)誤,因?yàn)槟形床捎肐GListAdapterDataSource協(xié)議。

通過將FeedViewController擴(kuò)展為采用IGListAdapterDataSource來解決此問題。 將以下內(nèi)容添加到文件的底部:

extension FeedViewController: IGListAdapterDataSource {

func objects(for listAdapter: IGListAdapter) -> [IGListDiffable] {

return loader.entries

}

func listAdapter(_ listAdapter: IGListAdapter, sectionControllerFor object: Any) -> IGListSectionController {

return IGListSectionController()}

func emptyView(for listAdapter: IGListAdapter) -> UIView? { return nil }

}

FeedViewController現(xiàn)在遵守IGListAdapterDataSource并實(shí)現(xiàn)其三個(gè)必需的方法:

objects(for:)返回應(yīng)該在集合視圖中顯示的數(shù)據(jù)對(duì)象數(shù)組。 這里提供了loader.entries,因?yàn)樗沼泿し咒洝?/p>

對(duì)于每個(gè)數(shù)據(jù)對(duì)象,listAdapter(_:sectionControllerFor :)必須返回一個(gè)節(jié)控制器的新實(shí)例。 現(xiàn)在,您正在返回一個(gè)簡(jiǎn)單的IGListSectionController來安撫編譯器 - 稍后,您將修改此命令以返回自定義日志部分控制器。

emptyView(for :)返回當(dāng)列表為空時(shí)應(yīng)顯示的視圖。 美國(guó)宇航局處于一個(gè)時(shí)間緊張狀態(tài),所以他們沒有預(yù)算這個(gè)功能。

創(chuàng)建第一個(gè)小組控制器

給定一個(gè)數(shù)據(jù)對(duì)象節(jié)控制器這個(gè)抽象概念,它可以在collection view的一部分中配置和控制單元格。 這個(gè)概念類似于配置視圖的view-model:數(shù)據(jù)對(duì)象是view-model,單元格是視圖。 小組控制器作為兩者之間的粘合劑。

在IGListKit中,您可以為不同類型的數(shù)據(jù)和行為創(chuàng)建一個(gè)新的小組控制器。 JPL工程師已經(jīng)建立了一個(gè)JournalEntry模型,所以你需要?jiǎng)?chuàng)建一個(gè)可以處理它的小組控制器。

右鍵單擊SectionControllers組,然后選擇新建文件...。 創(chuàng)建一個(gè)新的Cocoa Touch類,名為JournalSectionController,繼承IGListSectionController。



Xcode不會(huì)自動(dòng)導(dǎo)入第三方框架,所以在JournalSectionController.swift中添加一行:

import IGListKit

添加一下屬性

varentry: JournalEntry!

let solFormatter = SolFormatter()

JournalEntry是一個(gè)在實(shí)現(xiàn)數(shù)據(jù)源時(shí)將使用的模型類。 SolFormatter類提供了將日期轉(zhuǎn)換為Sol格式的方法。 你很快就需要。

另外在JournalSectionController中,通過添加以下內(nèi)容來覆蓋init():

override init() {

? ? super.init()

? ? inset = UIEdgeInsets(top: 0, left: 0, bottom: 15, right: 0)

}

如果沒有這樣的話,各部分之間的單元格將彼此相鄰。這將添加一個(gè)15點(diǎn)填充到JournalSectionController對(duì)象的底部。

您的小組控制器需要符合IGListSectionType協(xié)議,才能在IGListKit中使用。首先將以下擴(kuò)展名添加到文件的底部:

extension JournalSectionController: IGListSectionType {

func numberOfItems() -> Int {

return 2

}

func sizeForItem(at index: Int) -> CGSize {return .zero}

func cellForItem(at index: Int) -> UICollectionViewCell {return UICollectionViewCell()}

func didUpdate(to object: Any) {}

func didSelectItem(at index: Int) {}

您已經(jīng)實(shí)現(xiàn)了IGListSectionType協(xié)議的四個(gè)必需的方法。

numberOfItems默認(rèn)返回2,分別對(duì)應(yīng)日期和文本。 你可以參考一下ClassicFeedViewController.swift,collectionView(_:numberOfItemsInSection :)中也返回2, 這是一樣的

在doUpdate(到:)中,添加以下內(nèi)容:

entry =objectas? JournalEntry

didUpdate(to :)用于將對(duì)象傳遞給小組控制器。 注意,這種方法將始終在任何cell協(xié)議方法之前被調(diào)用。 在這里,保存?zhèn)魅氲膶?duì)象的數(shù)組

現(xiàn)在有了數(shù)據(jù),開始配置cell。 在cellForItem(at :)中實(shí)現(xiàn):

let cellClass: AnyClass = index == 0 ? JournalEntryDateCell.self : JournalEntryCell.self

let cell = collectionContext!.dequeueReusableCell(of: cellClass, for: self, at: index)

if let cell = cell as? JournalEntryDateCell {

cell.label.text = "SOL \(solFormatter.sols(fromDate: entry.date))"

} else if let cell = cell as? JournalEntryCell {

cell.label.text = entry.text

}

return cell

接下來,在 sizeForItem(at :) 中實(shí)現(xiàn):

func sizeForItem(at index: Int) -> CGSize {// 1

guard let context = collectionContext, let entry = entry else { return .zero }

// 2

let width = context.containerSize.width

// 3

if index == 0 {

return CGSize(width: width, height: 30)

} else {

return JournalEntryCell.cellSize(width: width, text: entry.text)

}

}

現(xiàn)在你有一個(gè)控制器接收一個(gè)JournalEntry對(duì)象并返回和大小兩個(gè)單元格。 現(xiàn)在是時(shí)候把它們整合在一起了。

返回到FeedViewController.swift中,將以下內(nèi)容替換為listAdapter(_:sectionControllerFor :)的內(nèi)容:

returnJournalSectionController()

現(xiàn)在編譯一下, 應(yīng)該可以看到和初始狀態(tài)一樣的樣子了

后面就是相似的步驟了,添加了兩種消息樣式, 完成后的Demo, 點(diǎn)擊這里


總結(jié)?

IGListKit 的優(yōu)點(diǎn)

1. 不要再次調(diào)用performBatchUpdates(_ :, completion :)或reloadData()

2. 更好的架構(gòu)與可重復(fù)使用的單元和組件

3. 創(chuàng)建具有多種數(shù)據(jù)類型的Collections

4. 解耦差分算法

5. 完全單元測(cè)試

6. 定制您的模型的差異行為

7. 簡(jiǎn)單的UICollectionView是其核心

8. 可擴(kuò)展API

9. 使用Objective-C語言,同時(shí)全面支持Swift

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,936評(píng)論 6 535
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,744評(píng)論 3 421
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,879評(píng)論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,181評(píng)論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,935評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,325評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,384評(píng)論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,534評(píng)論 0 289
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,084評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,892評(píng)論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,067評(píng)論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,623評(píng)論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,322評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,735評(píng)論 0 27
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,990評(píng)論 1 289
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,800評(píng)論 3 395
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,084評(píng)論 2 375

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

  • 今天星期天,去給一個(gè)廣順駕校做布場(chǎng),晚會(huì)非常的熱鬧,有很多節(jié)目表演,有唱歌的跳舞的,我也和好多演員的合了影,特別開...
    余果_閱讀 217評(píng)論 0 0
  • 高一的夏天,軍訓(xùn)時(shí)候第一次見到他。很黑很瘦壓根兒也談不上帥,只能勉強(qiáng)用陽光來形容。那時(shí)候我是長(zhǎng)發(fā),齊劉海。一改往...
    西川姑娘閱讀 465評(píng)論 16 4
  • 每個(gè)人心中,都住著一個(gè)女神。 我們小的時(shí)候,她很年輕、美麗, 有一天,她變老了, 有了白發(fā),走路也不再那么輕盈,歌...
    cbedd1eb589c閱讀 372評(píng)論 1 1
  • 曾經(jīng)在那風(fēng)吹蕩漾的年華里,其實(shí)沒有什么可以紀(jì)念的,但因?yàn)槟且粋€(gè)平凡的你,讓我深深的記住了你的容貌。 ...
    跟頭閱讀 203評(píng)論 4 3
  • 宋先生,你真的是太好太好太好了!我真的無法用語言來形容你的千萬般好!我真的想說宋先生,遇見你,是我最大的幸運(yùn)!這么...
    歆曼閱讀 525評(píng)論 0 2