作為一個iOS開發者,最常用的任務就是通過自定義cell的子類,來實現UITableView或者UICollectionView的自定義。并且 UITableView
和 UICollectionView
在注冊自定義cell子類這一塊都有非常類似的API:
public func registerClass(cellClass: AnyClass?, forCellWithReuseIdentifier identifier: String) public func registerNib(nib: UINib?, forCellWithReuseIdentifier identifier: String)
對于注冊cell的自定義最常用的解決辦法就是,聲明一個reuseIdentifier的常量,像下面這樣:
private let reuseIdentifier = quot;BookCellquot; class BookListViewController: UIViewController, UICollectionViewDataSource { @IBOutlet private weak var collectionView: UICollectionView! override func viewDidLoad() { super.viewDidLoad() let nib = UINib(nibName: quot;BookCellquot;, bundle: nil) self.collectionView.registerNib(nib, forCellWithReuseIdentifier: reuseIdentifier) } func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -gt; UICollectionViewCell { let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) if let bookCell = cell as? BookCell { // TODO: configure cell } return cell } }
接下來讓我們嘗試著使用泛型來讓它簡單化和安全化。
首先,如果在我們的代碼當中能不需要到處聲明reuse identifier常量,那就再好不過了。而事實上,我們可以直接使用自定義cell的類名來當做 默認的reuseIdentifier 。
我們可以通過創建一個Reuseable Views的協議并且創建默認的聲明方法給 UIView
的子類們。
protocol ReusableView: class { static var defaultReuseIdentifier: String { get } } extension ReusableView where Self: UIView { static var defaultReuseIdentifier: String { return NSStringFromClass(self) } } extension UICollectionViewCell: ReusableView { }
通過讓 UICollectionViewCell
遵循 ReusableView
協議,我們可以得到每個cell子類的一個唯一的重用標識。
let identifier = BookCell.defaultReuseIdentifier // identifier = quot;MyModule.BookCellquot;
接下來,我們通過同樣的方法,將注冊Nib步驟中的一些臟代碼給去除掉。
我們創建一個 Nib Loadable Views 的協議并通過協議拓展添加一個默認方法實現。
protocol NibLoadableView: class { static var nibName: String { get } } extension NibLoadableView where Self: UIView { static var nibName: String { return NSStringFromClass(self).componentsSeparatedByString(quot;.quot;).last! } } extension BookCell: NibLoadableView { }
通過讓我們的 BookCell
類遵循 NibLoadableView
協議,現在我們就有了一個更安全和方便的方法去獲得到Nib的名稱。
let nibName = BookCell.nibName // nibName = quot;BookCellquot;
有這兩個協議,我們可以通過使用 Swift的泛型
并且通過拓展 UICollectionView
來簡化cell的注冊和使用。
extension UICollectionView { func registerlt;T: UICollectionViewCell where T: ReusableViewgt;(_: T.Type) { registerClass(T.self, forCellWithReuseIdentifier: T.defaultReuseIdentifier) } func registerlt;T: UICollectionViewCell where T: ReusableView, T: NibLoadableViewgt;(_: T.Type) { let bundle = NSBundle(forClass: T.self) let nib = UINib(nibName: T.nibName, bundle: bundle) registerNib(nib, forCellWithReuseIdentifier: T.defaultReuseIdentifier) } func dequeueReusableCelllt;T: UICollectionViewCell where T: ReusableViewgt;(forIndexPath indexPath: NSIndexPath) -gt; T { guard let cell = dequeueReusableCellWithReuseIdentifier(T.defaultReuseIdentifier, forIndexPath: indexPath) as? T else { fatalError(quot;Could not dequeue cell with identifier: \(T.defaultReuseIdentifier)quot;) } return cell } }
注意這里,我們創建了兩個版本的注冊方法,一個是用來注冊 ReusableView
子類用的,一個是用來注冊 ReusableView
和 NibLoadableView
的子類。這很好的將view controller的特定的注冊方法分離出來。
另一個比較棒的細節就是 dequeueReusableCell
方法不再需要給他任何重用標識字符串而且可以直接使用cell的子類作為返回值。
現在cell的注冊和使用代碼看起來棒極了 :) 。
class BookListViewController: UIViewController, UICollectionViewDataSource { @IBOutlet private weak var collectionView: UICollectionView! override func viewDidLoad() { super.viewDidLoad() self.collectionView.register(BookCell.self) } func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -gt; UICollectionViewCell { let cell: BookCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) // TODO: configure cell return cell } ... }
總結
如果你是從 Objective-C 轉到 Swift 的話,研究Swift強大的新特性比如 協議拓展 、 泛型 , 從而找到更優雅的實現方式和替代方法是非常值得的。
Tags: Swift 泛型
文章來源:http://shixiong.name/yong-swift-xie-yi-kuo-zhan-he