[譯]用 Swift 協議擴展和泛型來實現復用

分類:技術 時間:2016-10-25

作為一個iOS開發者,最常用的任務就是通過自定義cell的子類,來實現UITableView或者UICollectionView的自定義。并且 UITableViewUICollectionView 在注冊自定義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 子類用的,一個是用來注冊 ReusableViewNibLoadableView 的子類。這很好的將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


ads
ads

相關文章
ads

相關文章

ad