Reusable
Reusable是一個在swift下使用的開源庫。利用protocol extension結合泛型提供了一個優雅的方案來dequeueReusableCell。
使用
根據型別獲取cell
讓你的cell宣告Reusable
或NibReusable
協議
1 2 3 4 5 |
//如果cell定義在xib中,宣告NibReusable class MyCustomCell: UITableViewCell, NibReusable { } //如果cell是基於純程式碼的,宣告Reusable class MyCustomCell: UITableViewCell, Reusable { } |
接著在tableview或者collectionView中register
1 |
tableView.registerReusableCell(MyCustomCell) |
粗暴的直接獲取cell就可以啦:
1 2 3 4 5 |
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell: MyCustomCell = tableView.dequeueReusableCell(indexPath: indexPath) … // configure the cell, which is already of the expected MyCustomCell type return cell } |
是的。你沒有看錯,這樣就能獲取到這個型別的reuse cell,不需要傳入reuseIdentifiers,不需要UITableViewCell型別強轉。
根據型別獲取xib中的UIView物件
UIView物件宣告NibLoadable
協議。
利用MyCustomView.loadFromNib()
就可以從“MyCustomView.xib”中例項化返回MyCustomView的例項物件
根據型別獲取Storyboards中的UIViewController物件
UIViewController物件宣告StoryboardBased
或者StoryboardSceneBased
協議。
利用YourCustomViewController.instantiate()
就可以從Storyboard中例項化返回例項物件
實現
核心的思路其實很簡單,就是利用自己的類名做為重用識別符號。
我們就來定義一個協議,宣告一個靜態變數reuseIdentifier,並實現extension,預設識別符號返回當前類名:
1 2 3 4 5 6 7 8 9 10 11 |
protocol Reusable: class { static var reuseIdentifier: String { get } } extension Reusable { static var reuseIdentifier: String { // I like to use the class's name as an identifier // so this makes a decent default value. return String(Self) } } |
接著我們給tableview的寫一個自定義獲取reuse cell的擴充套件方法:
1 2 3 |
func dequeueReusableCell(indexPath indexPath: NSIndexPath) -> T { return self.dequeueReusableCellWithIdentifier(T.reuseIdentifier, forIndexPath: indexPath) as! T } |
注意這個泛型引數,這個泛型是根據返回值的型別來確定的。所以返回的cell必須實現Reusable協議。我們將這型別裡的那個靜態變數T.reuseIdentifier作為Identifier。
我們當然還要同時改造register方法。
1 2 3 4 5 |
public extension UITableView { final func registerReusableCell(cellType: T.Type) { self.registerClass(cellType.self, forCellReuseIdentifier: cellType.reuseIdentifier) } } |
這個時候我們忽然意識到,還有registerNib
沒有解決。
思路也是相似的,給協議再增加一個返回nib物件的靜態變數唄。就像這樣:
1 2 3 4 |
protocol Reusable: class { static var reuseIdentifier: String { get } static var nib: UINib? { get } } |
實現是這樣:
1 2 3 |
static var nib: UINib { return UINib(nibName: String(self), bundle: NSBundle(forClass: self)) } |
但是這裡再往深一點想,其實載入nib和reuseIdentifier是兩件事,因為我們有時也會從xib獲取其他UIView的物件。protocol也提供了組合的特性。所以我們可以把獲取nib單獨拆出來。
1 2 3 4 5 6 |
public protocol NibLoadable: class { /// The nib file to use to load a new instance of the View designed in a XIB static var nib: UINib { get } } public protocol NibReusable: Reusable, NibLoadable {} |
這樣最後一塊拼圖就有了:
1 2 3 |
final func registerReusableCell(cellType: T.Type) { self.registerNib(cellType.nib, forCellReuseIdentifier: cellType.reuseIdentifier) } |
接著再順手給UIView寫一個根據型別獲取例項的擴充套件方法:
1 2 3 4 5 6 7 8 9 |
public extension NibLoadable where Self: UIView { static func loadFromNib() -> Self { guard let view = nib.instantiateWithOwner(nil, options: nil).first as? Self else { fatalError("The nib \(nib) expected its root view to be of type \(self)") } return view } } |
歡迎關注我的微博:@沒故事的卓同學