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 } } |
欢迎关注我的微博:@没故事的卓同学