【譯】更加 Swift 化的 Collection View 和 Table View Cells

作者:Jameson Quave搓逾,原文鏈接,原文日期:2015/12/28
譯者:CMB蛹疯;校對:Cee荸镊;定稿:千葉知風(fēng)

這是一個常見的場景:你有一個 tableView 或者一個 collectionView,并且里面含有大量不同種類的內(nèi)容阅束。你想做到基于不同種類的內(nèi)容而展示不一樣的 cell 呼胚,而且這些 cell 都混合在同一個部件里(原諒我站在藝術(shù)的角度去設(shè)計),它看起來就如下圖所示:

在 Objective-C 中息裸,最典型就是使用 NSArray 來記錄 collectionView 的數(shù)據(jù)源蝇更,然后通過對比每個數(shù)據(jù)源的類型后再對 cell 進行操作沪编,現(xiàn)在看來這種方式是特別不方便的。

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {

    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"identifier" forIndexPath:indexPath];
 
    id record = self.records[indexPath.row];
 
    if([record isKindOfClass:[PlaythroughItem class]]) {
        // ...
    }
    else if([record isKindOfClass:[ReviewItem class]]) {
        // ...
    }
    else if([record isKindOfClass:[TrailerItem class]]) {
        // ...
    }
 
    return cell;
}

戰(zhàn)栗吧

這并不是種類型安全的方法年扩,盡管我們在 Objective-C 中這么使用這段代碼已經(jīng)不足為奇了蚁廓。在 Swift 中,有更加好的替換方式去解決上述問題厨幻,那就是使用枚舉的 case 情況來為不同類型的項做標識相嵌,然后通過這些 case 就可以找到我們所需要的項。讓我們看看下面的例子况脆。

例子

這是一個我正在寫的休閑娛樂類 App 中需要一些不同新聞類型的 cell 的代碼:

enum NewsItem {
  case Trailer(index: Int)
  case Review(index: Int)
  case Playthrough(index: Int)
}

索引僅僅是用來記錄數(shù)據(jù)在數(shù)據(jù)庫中位置的方法饭宾。我們采取這種索引的方法來標識所需數(shù)據(jù)在 collectionView 中位置的展示。對于特定視頻格了,我們就不需要其所關(guān)聯(lián)的所有數(shù)據(jù)了看铆,所需要的信息僅需要在 collectionView 中的 cell 點擊之后才去通過索引獲取。

我們有一個簡單的 collectionView盛末,它里面含有三個自定義的 cell 弹惦。我使用 NewsFeed.swift 文件作為這個新聞 collectionView 的主要數(shù)據(jù)源。我特別感興趣的是 cellForItemAtIndexPath 方法悄但,通過 NewsItem 枚舉來區(qū)分 record 的類型棠隐,從而產(chǎn)生相對應(yīng)的 cell

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {

    let record = records[indexPath.row]
 
    switch(record) {
 
    case .Playthrough(let index): 
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier("PlaythroughCell", forIndexPath: indexPath) as! PlaythroughCollectionViewCell
        let playthrough = MediaDB.playthroughAtIndex(index)
        cell.titleLabel.text = playthrough.title
        cell.lengthLabel.text = playthrough.length.prettyTime
        return cell
 
    case .Review(let index):
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier("ReviewCell", forIndexPath: indexPath) as! ReviewCollectionViewCell
        let review = MediaDB.reviewAtIndex(index)
        cell.ratingLabel.text = "\(review.rating) out of 10"
        cell.titleLabel.text = review.title
        return cell
 
    case .Trailer(let index):
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier("TrailerCell", forIndexPath: indexPath) as! TrailerCollectionViewCell
        let trailer = MediaDB.trailerAtIndex(index)
        cell.titleLabel.text = trailer.title
        cell.lengthLabel.text = trailer.length.prettyTime
        return cell
    }
}

上面的代碼可以清晰看出,record 可以表示為 NewsItem 枚舉里三個 case 中任意一個:

enum NewsItem {
  case Trailer(index: Int)
  case Review(index: Int)
  case Playthrough(index: Int)
}

當(dāng)我們想在 collectionView 中展示一個 cell 的時候檐嚣,我們可以通過相關(guān)的索引值去找到數(shù)據(jù)庫中所對應(yīng)的那一項宵荒。

這段代碼讓我覺得很不舒服。有許多重復(fù)代碼净嘀,尤其是 switch 顯得非常笨重报咳,在每個 case 中都做了太多事情。

但是挖藏,如果我創(chuàng)建了一個可以用在 collectionView cell 上的處理任何數(shù)據(jù)源的協(xié)議呢暑刃?鑒于每個視圖(view)都并不相同,所以我不希望這個協(xié)議在模型(model)中使用膜眠。但我可以在特定的 collectionView cell 的子類上使用它岩臣。

所以,我創(chuàng)建了一個叫做 NewsCellPresentable 協(xié)議宵膨,這個協(xié)議被自定義的 collectionView cell 所擴展:

protocol NewsCellPresentable {
    func configureForIndex(index: Int)
}
 
extension PlaythroughCollectionViewCell: NewsCellPresentable {
    func configureForIndex(index: Int) {
        let playthrough = MediaDB.playthroughAtIndex(index)
        self.titleLabel.text = playthrough.title
        self.lengthLabel.text = playthrough.length.prettyTime
    }
}
extension ReviewCollectionViewCell: NewsCellPresentable {
    func configureForIndex(index: Int) {
        let review = MediaDB.reviewAtIndex(index)
        self.titleLabel.text = review.title
        self.ratingLabel.text = "\(review.rating) out of 10"
    }
}
extension TrailerCollectionViewCell: NewsCellPresentable {
    func configureForIndex(index: Int) {
        let trailer = MediaDB.trailerAtIndex(index)
        self.titleLabel.text = trailer.title
        self.lengthLabel.text = trailer.length.prettyTime
    }
}

這樣寫看起來已經(jīng)很簡潔明了了〖芑眩現(xiàn)在我們回到 cellForItemAtIndexPath 方法中對代碼進行修改,修改后如下所示:

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
    let record = records[indexPath.row]
 
    var cell: NewsCellPresentable
    switch(record) {
 
    case .Playthrough(let index):
        cell = collectionView.dequeueReusableCellWithReuseIdentifier("PlaythroughCell", forIndexPath: indexPath) as! PlaythroughCollectionViewCell
        cell.configureForIndex(index)
 
    case .Review(let index):
        cell = collectionView.dequeueReusableCellWithReuseIdentifier("ReviewCell", forIndexPath: indexPath) as! ReviewCollectionViewCell
        cell.configureForIndex(index)
 
    case .Trailer(let index):
        cell = collectionView.dequeueReusableCellWithReuseIdentifier("TrailerCell", forIndexPath: indexPath) as! TrailerCollectionViewCell
        cell.configureForIndex(index)
    }
 
    return (cell as! MediaCollectionViewCell)
}

你覺得這種方法怎么樣辟躏?這是一種更為簡潔的方法嗎谷扣?如果你有其它不同的實現(xiàn)方法,可以直接在文章下面留言給我捎琐,或者在 Twitter 上留言給我会涎,我的用戶名是 @jquave裹匙,希望可以一起交流學(xué)習(xí)。

附言

如果你沒有數(shù)據(jù)庫底層代碼末秃,但又想寫出和我例子一樣的實例概页,你可以參照下列代碼:

class MediaDB {
    class func titleForRecord(index: Int) -> String {
        return "Title!!"
    }
    class func trailerAtIndex(index: Int) -> Trailer {
        return Trailer()
    }
    class func reviewAtIndex(index: Int) -> Review {
        return Review()
    }
    class func playthroughAtIndex(index: Int) -> Playthrough {
        return Playthrough()
    }
}
 
struct Trailer {
    let title = "Trailer Title"
    let length = 190
}
 
struct Review {
    let title = "Review Title"
    let rating = 4
}
 
struct Playthrough {
    let title = "Playthrough Title"
    let length = 9365
}
 
 
enum NewsItem {
    case Trailer(index: Int)
    case Review(index: Int)
    case Playthrough(index: Int)
}

就個人而言,在寫后端服務(wù)和接口之前练慕,我總會做靜態(tài)值的存根惰匙。這樣會使得項目更容易迭代。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末铃将,一起剝皮案震驚了整個濱河市徽曲,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌麸塞,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件涧衙,死亡現(xiàn)場離奇詭異哪工,居然都是意外死亡,警方通過查閱死者的電腦和手機弧哎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門雁比,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人撤嫩,你說我怎么就攤上這事偎捎。” “怎么了序攘?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵茴她,是天一觀的道長。 經(jīng)常有香客問我程奠,道長丈牢,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任瞄沙,我火速辦了婚禮己沛,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘距境。我一直安慰自己申尼,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布垫桂。 她就那樣靜靜地躺著师幕,像睡著了一般。 火紅的嫁衣襯著肌膚如雪诬滩。 梳的紋絲不亂的頭發(fā)上们衙,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天钾怔,我揣著相機與錄音,去河邊找鬼蒙挑。 笑死宗侦,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的忆蚀。 我是一名探鬼主播矾利,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼馋袜!你這毒婦竟也來了男旗?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤欣鳖,失蹤者是張志新(化名)和其女友劉穎察皇,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體泽台,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡什荣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了怀酷。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片稻爬。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖蜕依,靈堂內(nèi)的尸體忽然破棺而出桅锄,到底是詐尸還是另有隱情,我是刑警寧澤样眠,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布友瘤,位于F島的核電站,受9級特大地震影響檐束,放射性物質(zhì)發(fā)生泄漏商佑。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一厢塘、第九天 我趴在偏房一處隱蔽的房頂上張望茶没。 院中可真熱鬧,春花似錦晚碾、人聲如沸抓半。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽笛求。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間探入,已是汗流浹背狡孔。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蜂嗽,地道東北人苗膝。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像植旧,于是被迫代替她去往敵國和親辱揭。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,685評論 2 360

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