作者:Olivier Halligon害捕,原文鏈接,原文日期:2016-01-06
譯者:walkingway曙痘;校對:小鍋腰耙;定稿:numbbbbb
各位新年快樂????????! 2016 年第一篇博客我想分享一個非常有用的技巧源请,那就是向大家展示 Swift 泛型的強大枪芒,以及方便地使用泛型來處理 UITableViewCells
和 UICollectionViewCells
。
介紹
我不喜歡使用字符串做標(biāo)識符谁尸,我認(rèn)為使用常量要比字符串好很多舅踪。
但是,當(dāng)涉及到 UITableViewCell
或 UICollectionViewCell
以及他們的重用標(biāo)識符(reuseIdentifiers
)時良蛮,我想采用一種更加魔幻的解決方案:『使用 Swift 的泛型 + Mixins 的方式』抽碌,下面讓我們摒住呼吸,見證奇跡的時刻决瞳。
魔法時刻
我的想法是在 UITableViewCell
(或 UICollectionViewCell
)的子類中將 reuseIdentifier
聲明為一個靜態(tài)常量货徙,然后用它使這個 cell 的實例對外部透明化(即,不用顯式地使用 reuseIdentifer
來實例化 cell)皮胡。
我們首先聲明一個協(xié)議痴颊,以便于稍后能夠將其作為 Mixin 來使用:
protocol Reusable: class {
static var reuseIdentifier: String { get }
}
extension Reusable {
static var reuseIdentifier: String {
// 我喜歡使用類名來作為標(biāo)識符
// 所以這里可以用類名返回一個默認(rèn)值
return String(Self)
}
}
當(dāng)我們使用泛型實現(xiàn) dequeueReusableCell(…)
方法時,魔法出現(xiàn)了:
func dequeueReusableCell<T: Reusable>(indexPath indexPath: NSIndexPath) -> T {
return self.dequeueReusableCellWithIdentifier(T.reuseIdentifier, forIndexPath: indexPath) as! T
}
得益于 Swift 的類型推斷屡贺,這個方法將使用調(diào)用點的上下文來推斷 T 的實際類型蠢棱,因此這個類型可以被看做方法實現(xiàn)中的『復(fù)古注入』(retro-injected)!?
let cell = tableView.dequeueReusableCell(indexPath: indexPath) as MyCustomCell
注意觀察
reuseIdentifier
在內(nèi)部的使用方法...完全由編譯器看到的返回類型決定甩栈!那就是我說的類型“復(fù)古注射(retro-injected)”泻仙,以及為什么我超??它的原因
譯者注:Swift 的類型在編譯時刻就確定了,所以當(dāng)你寫下
as MyCustomCell
后量没,cell 的類型即泛型 T 的具體類型就確定了
很美妙不是嗎饰豺?
更進一步
當(dāng)然,除了 dequeueReusableCellWithIdentifier
之外允蜈,你同樣可以把這個方法用在 registerNib(_, forCellWithReuseIdentifier:)
和 UICollectionViewCells
,supplementary 視圖上蒿柳。
UITableViewCells
和 UICollectionViewCells
都能通過類名(registerClass(_, forCellWithReuseIdentifier:)
) 或 nib(registerNib(_, forCellWithReuseIdentifier:)
)的方式進行注冊饶套。如果存在 nib,我們將在協(xié)議中添加一個類型屬性(static var nib: UINib?
)垒探,然后使用這個 nib 注冊 cell妓蛮;如果 nib 不存在則使用類注冊。
代碼
這里是我在項目中實際使用的代碼:
[2016-01-20 修改]
現(xiàn)在可以在Github上看到這個代碼了圾叼!
import UIKit
protocol Reusable: class {
static var reuseIdentifier: String { get }
static var nib: UINib? { get }
}
extension Reusable {
static var reuseIdentifier: String { return String(Self) }
static var nib: UINib? { return nil }
}
extension UITableView {
func registerReusableCell<T: UITableViewCell where T: Reusable>(_: T.Type) {
if let nib = T.nib {
self.registerNib(nib, forCellReuseIdentifier: T.reuseIdentifier)
} else {
self.registerClass(T.self, forCellReuseIdentifier: T.reuseIdentifier)
}
}
func dequeueReusableCell<T: UITableViewCell where T: Reusable>(indexPath indexPath: NSIndexPath) -> T {
return self.dequeueReusableCellWithIdentifier(T.reuseIdentifier, forIndexPath: indexPath) as! T
}
func registerReusableHeaderFooterView<T: UITableViewHeaderFooterView where T: Reusable>(_: T.Type) {
if let nib = T.nib {
self.registerNib(nib, forHeaderFooterViewReuseIdentifier: T.reuseIdentifier)
} else {
self.registerClass(T.self, forHeaderFooterViewReuseIdentifier: T.reuseIdentifier)
}
}
func dequeueReusableHeaderFooterView<T: UITableViewHeaderFooterView where T: Reusable>() -> T? {
return self.dequeueReusableHeaderFooterViewWithIdentifier(T.reuseIdentifier) as! T?
}
}
extension UICollectionView {
func registerReusableCell<T: UICollectionViewCell where T: Reusable>(_: T.Type) {
if let nib = T.nib {
self.registerNib(nib, forCellWithReuseIdentifier: T.reuseIdentifier)
} else {
self.registerClass(T.self, forCellWithReuseIdentifier: T.reuseIdentifier)
}
}
func dequeueReusableCell<T: UICollectionViewCell where T: Reusable>(indexPath indexPath: NSIndexPath) -> T {
return self.dequeueReusableCellWithReuseIdentifier(T.reuseIdentifier, forIndexPath: indexPath) as! T
}
func registerReusableSupplementaryView<T: Reusable>(elementKind: String, _: T.Type) {
if let nib = T.nib {
self.registerNib(nib, forSupplementaryViewOfKind: elementKind, withReuseIdentifier: T.reuseIdentifier)
} else {
self.registerClass(T.self, forSupplementaryViewOfKind: elementKind, withReuseIdentifier: T.reuseIdentifier)
}
}
func dequeueReusableSupplementaryView<T: UICollectionViewCell where T: Reusable>(elementKind: String, indexPath: NSIndexPath) -> T {
return self.dequeueReusableSupplementaryViewOfKind(elementKind, withReuseIdentifier: T.reuseIdentifier, forIndexPath: indexPath) as! T
}
}
示例用法
下面演示如何聲明一個 UITableViewCell
的子類:
class CodeBasedCustomCell: UITableViewCell, Reusable {
// By default this cell will have a reuseIdentifier or "MyCustomCell"
// unless you provide an alternative implementation of `var reuseIdentifier`
// ...
}
class NibBasedCustomCell: UITableViewCell, Reusable {
// Here we provide a nib for this cell class
// (instead of relying of the protocol's default implementation)
static var nib: UINib? {
return UINib(nibName: String(NibBasedCustomCell.self), bundle: nil)
}
// ...
}
然后在 UITableViewDelegate
/UITableViewDataSource
中使用他們:
class MyTableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
tableView.registerReusableCell(CodeBasedCustomCell.self) // This will register using the class without using a UINib
tableView.registerReusableCell(NibBasedCustomCell.self) // This will register using NibBasedCustomCell.xib
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: UITableViewCell
if indexPath.section == 0 {
cell = tableView.dequeueReusableCell(indexPath: indexPath) as CodeBasedCustomCell
} else {
cell = tableView.dequeueReusableCell(indexPath: indexPath) as NibBasedCustomCell
}
return cell
}
}
另一種解決方案
有些人傾向于將 Reusable
協(xié)議拆分成兩個不同的協(xié)議蛤克,用于區(qū)分基于 nib 創(chuàng)建與基于 class 創(chuàng)建的 cell:
protocol Reusable: class {
static var reuseIdentifier: String { get }
}
extension Reusable {
static var reuseIdentifier: String { return String(Self) }
}
protocol NibReusable: Reusable {
static var nib: UINib { get }
}
extension NibReusable {
static var nib: UINib {
return UINib(nibName: String(Self), bundle: nil)
}
}
這樣基于 nib 創(chuàng)建的 cell 也能使用默認(rèn)實現(xiàn) —— 因此不用在子類中再重新實現(xiàn)一遍捺癞。
但是這也會迫使你在 UITableView
和 UICollectionView
上添加更多的實現(xiàn)方法(每個協(xié)議中添加一個實現(xiàn)),所以嘛...這其中的平衡還要靠你自己來把握 ???
福利:現(xiàn)在你能通過 Cocoapods 使用這個庫啦 ??
2016-01-20 增加
以上代碼和示例构挤,我已經(jīng)上傳至 GitHub髓介,并且通過 Swift Package 以及 CocoaPod 發(fā)布了,現(xiàn)在可以很方便地添加到你的工程中筋现。
隨時歡迎各種 PR 來共同改進這個項目 ??
--
最后希望你喜歡這一技巧唐础,我們下次再見嘍!??
本文由 SwiftGG 翻譯組翻譯矾飞,已經(jīng)獲得作者翻譯授權(quán)一膨,最新文章請訪問 http://swift.gg。