UICollectionView 與 Core Data 配合

實質(zhì)上是 UICollectionView 與 NSFetchedResultsController 配合期虾,在去年遇到這個問題時镶苞,當(dāng)時進行了一番嘗試無解,找到了倆年前 @ash furrow 的方案宾尚,但原來的方案中無法同時處理 section 和 cell 的變化,我針對這個缺陷做了改進牛郑,但復(fù)雜而且難以使用敬鬓;后來 @SixtyFrames?修正了這個缺陷,并且優(yōu)化了原方案的邏輯础芍,幾乎不需要改動就可使用杈抢。原方案的作者后來停止了維護該方案,并推薦了更優(yōu)雅的解決方案:JSQDataSourcesKit仑性,使用前提是 iOS 8和 Swift 2.0惶楼。?

我上周 fork 了這個庫,原本還打算給它貢獻點這個話題方面的代碼诊杆,結(jié)果發(fā)現(xiàn)它已經(jīng)處理好了歼捐,而且代碼封裝得很好,我目前可寫不出這種水平的代碼晨汹。使用 Swift 有三個月左右了豹储,越來越喜歡這個語言了,最近連 OC 都不會寫了淘这。剛開始總會接觸到 protocol 的話題剥扣,但不是很理解慨灭,最近寫了幾個動畫,對 protocol 有點感受了,不過這個庫我目前看得有點吃力,不知道為何要那樣封裝宋彼,等三個月再看我的水平是否足夠±晨玻現(xiàn)成的解決方案是有了碴卧,但思路還是值得記錄的烫葬。

為 NSFetchedResultsController 對象提供 delegate 后,就能夠跟蹤數(shù)據(jù)的變化并更新視圖了, delegate 對象需要實現(xiàn)下面的方法:

-controllerWillChangeContent:

-controller:didChangeSection:atIndex:forChangeType:

-controller:didChangeObject:atIndexPath:forChangeType:newIndexPath:

-controllerDidChangeContent:

當(dāng) NSManagedObjectContext 中對象發(fā)生了變化后,上面四個方法除了中間的兩個按照變化的情況來調(diào)用之外,基本上是按照順序來調(diào)用的。

對于 UICollectionView,來看看解決思路,ash furrow 指出問題的關(guān)鍵在于:

The trick is to queue the updates made through the NSFetchedResultsControllerDelegate until the controller finishes its updates. UICollectionView doesn't have the same beginUpdates and endUpdates that UITableView has to let it work easily with NSFetchedResultsController, so you have to queue them or you get internal consistency runtime exceptions.

那么解決的方法就是在上面中間兩個 didChangeXXX 方法中搜集變化的內(nèi)容汰寓,然后在最后的 didChangeContent 里使用 UICollectionView 的 performBatchUpdates: 方法來集中更新 UI俺孙。?

原方案沒有考慮到一些情況而無法處理同時發(fā)生了 section 和 cell 變化的情況荣茫,比如我的需求是批量操作多個 section 內(nèi)的 cell旨剥,例如优幸,選中不同 section 內(nèi)的 cell备典,這包括了某個 section 內(nèi)的全部 cell 以及其他 section 中部分 cell,然后新建一個 section,如下圖:

?忽略圖中的Bug

那么 delegate 將會接受到以下通知满哪,log 里我打印出了動作類型以及位置變化哈恰,使用 S 代表了 cell 所在的 section锌云,I 代表 row兼贡。

Insert Section at Index: 4

Delete Section at Index: 2

Move Cell from S1I8 -> S4I5

Move Cell from S2I1 -> S4I1

Move Cell from S2I0 -> S4I0

Move Cell from S4I1 -> S4I2

Move Cell from S2I2 -> S4I4

Move Cell from S3I1 -> S4I3

上面的這些變化需要我們在 controllerDidChangeContent: 里先對這些變化進行分類和過濾后再來更新 UI涉兽。你需要嘗試一些操作來測試 UICollectionView 是怎么判定操作的類型的,依據(jù)這個才好對操作進行分類和過濾篙程。

操作類型有這么四種:

NSFetchedResultsChangeInsert?

NSFetchedResultsChangeDelete

NSFetchedResultsChangeMove

NSFetchedResultsChangeUpdate

NSFetchedResultsChangeMove 這種操作類型的通知里會包含目標的源位置 fromIndexPath 和新位置 toIndexPath虱饿,根據(jù)對這種操作類型的處理方式不同,有兩種方法來處理和過濾這些變化:

1. @SixtyFrames?的方案:

過濾階段:

首先對標記為 NSFetchedResultsChangeMove 的操作進行過濾:

- fromIndexPath 的 section 在被刪除的 section 集合里并且 toIndexPath 的 section 不在新增的 section 集合里,則被視為 NSFetchedResultsChangeInsert礁苗,并將 toIndexPath 納入該操作類型的集合里。翻譯一下就是:從被刪除的 section 移動到另外一個已知的 section 被判定為 insert。

上面例子中的沒有符合這個條件的昵时。

- fromIndexPath 的 section 不在被刪除的 section 集合里并且 toIndexPath 的 section 在新增的 section 集合里,被視為 NSFetchedResultsChangeDelete壹甥,并將 fromIndexPath 納入該操作類型的集合中救巷。意思就是:整個 section 沒有被刪的而且移動到新 section 的被判定為 delete。

上面的例子中有三條符合: S1I8 -> S4I5, S3I1 -> S4I3, S4I1 -> S4I2句柠。S1I8浦译,S3I1,S4I1 這些 NSIndexPath 被標記為要被刪除的目標溯职。

- fromIndexPath 的 section 不在被刪除的 section 集合里并且 toIndexPath 的 section 不在新增的 section 集合里精盅,才被視為 NSFetchedResultsChangeMove。意思就是:從一個已知的 section 移動到另外一個已知的 section 才被判定為真正的 move谜酒。

上面的例子沒有符合條件的叹俏。

其次,對標記為 NSFetchedResultsChangeDelete 的操作進行過濾甚带,去除那些目標 section 在被刪除的 section 集合里的 NSIndexPath她肯。這么做的原因是,某個 section 要被刪除的話鹰贵,該 section 內(nèi)的所有 cell 都會被移除晴氨,但不用分別刪除 section 和刪除 cell 這樣的重復(fù)操作,因此必須把該 section 內(nèi)的所有 NSIndexPath 過濾掉碉输。

上面的例子中沒有 NSIndexPath 被移除籽前。

最后,對標記為 NSFetchedResultsChangeInsert 的操作進行過濾,去除那些目標 section 是新增 section 的 NSIndexPath枝哄。

上面的例子里沒有屬于 NSFetchedResultsChangeInsert 的 NSIndexPath肄梨,因此什么也不會移除。

接下來要對上面的過濾結(jié)果進行針對性的操作挠锥,更新 UI 要按照一定的順序來众羡,這點在 performBatchUpdates:completion: 的文檔里有說明:delete 操作必須在 insert 之前,因為 insert 時的位置是相對于 delete 后再次計算的蓖租,這與我們之前看到的 log 恰好是反過來的粱侣。

另外,section 的變化會同時處理該 section 內(nèi)所有 cell 的變化蓖宦,所有只需要對 section 進行更新即可齐婴。

- 先處理 section 的變化:先刪除被標記的 section(可能有多個),然后添加被標記的 section(一般情況下只有一個)稠茂。

上面的例子 Section 2 被整體刪除柠偶,然后添加 Section 4。

- 然后處理不在 section 變化范圍內(nèi)的漏網(wǎng)之魚睬关,處理順序是诱担,先處理刪除,然后是添加共螺,移動和更新操作的順序無所謂该肴,因為這兩個不影響整體的布局。

上面的例子里被判定為 delete 的三個目標將被刪除藐不。

至此結(jié)束匀哄。

2. JSQDataSourcesKit

JSQDataSourcesKit 的方式與上面截然不同,簡直有種做奧賽題使用常規(guī)方法和特殊方法的區(qū)別:把 NSFetchedResultsChangeMove 拆分為先 Delete 再 Insert雏蛮,這樣一來所有的操作只剩下 Delete 和 Insert(Update 不影響大局)涎嚼,因此只需要對搜集的變化進行分類而不需要過濾,而且在處理上不需要依賴特定的順序挑秉。在處理時法梯,需要先處理 object 的變化,再處理 section 的變化犀概。處理 object 的變化時立哑,遇到 Move 操作時,先刪除原來的目標姻灶,在添加新的目標铛绰;而處理 section 的變化時,不處理 Move 操作产喉。

簡潔有力捂掰!但老實說敢会,除了拆分這個操作明白,后來對變化的處理不明白这嚣。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末鸥昏,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子姐帚,更是在濱河造成了極大的恐慌吏垮,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件罐旗,死亡現(xiàn)場離奇詭異惫皱,居然都是意外死亡,警方通過查閱死者的電腦和手機尤莺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來生棍,“玉大人颤霎,你說我怎么就攤上這事⊥康危” “怎么了友酱?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長柔纵。 經(jīng)常有香客問我缔杉,道長,這世上最難降的妖魔是什么搁料? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任或详,我火速辦了婚禮,結(jié)果婚禮上郭计,老公的妹妹穿的比我還像新娘霸琴。我一直安慰自己,他們只是感情好昭伸,可當(dāng)我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布梧乘。 她就那樣靜靜地躺著,像睡著了一般庐杨。 火紅的嫁衣襯著肌膚如雪选调。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天灵份,我揣著相機與錄音仁堪,去河邊找鬼。 笑死各吨,一個胖子當(dāng)著我的面吹牛枝笨,可吹牛的內(nèi)容都是我干的袁铐。 我是一名探鬼主播,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼横浑,長吁一口氣:“原來是場噩夢啊……” “哼剔桨!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起徙融,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤洒缀,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后欺冀,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體树绩,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年隐轩,在試婚紗的時候發(fā)現(xiàn)自己被綠了饺饭。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡职车,死狀恐怖瘫俊,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情悴灵,我是刑警寧澤扛芽,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站积瞒,受9級特大地震影響川尖,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜茫孔,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一叮喳、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧银酬,春花似錦嘲更、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至李破,卻和暖如春宠哄,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背嗤攻。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工毛嫉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人妇菱。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓承粤,卻偏偏與公主長得像暴区,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子辛臊,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,779評論 2 354

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