優雅的使用UITableView(Swift 中)

優雅的使用UITableView(OC 上)中,已經給你們分享了怎麼使用UITableView,優雅的構建一個頁面。git

在這一節,主要和你們聊一聊這兩點:

  1. 怎麼把優雅的使用UITableView(OC 上)的思路搬到Swift
  2. 泛型Any的區別
  3. 在Swift中優雅的使用UITableView構建List頁面

怎麼把優雅的使用UITableView(OC 上)的思路搬到Swift

再回憶一下這張圖 github

其中關鍵的點其實就是Row,若是咱們把Row作好了,其實大功基本告成。編程

看當作果:swift

Swift版的是否是更優雅了些?數組

Row的實現

struct NoneItem {}

protocol Updatable: class {
    
    associatedtype ViewData
    
    func update(viewData: ViewData)
}

extension Updatable {
    func update(viewData: NoneItem) {}
}

protocol RowType {
    
    var tag: RowTag { get }

    var reuseIdentifier: String { get }
    var cellClass: AnyClass { get }

    func update(cell: UITableViewCell)

    func cell<C: UITableViewCell>() -> C
    func cellItem<M>() -> M
}

class Row<Cell> where Cell: Updatable, Cell: UITableViewCell {

    let tag: RowTag

    let viewData: Cell.ViewData
    let reuseIdentifier = "\(Cell.classForCoder())"
    let cellClass: AnyClass = Cell.self
    
    init(viewData: Cell.ViewData, tag: RowTag = .none) {
        self.viewData = viewData
        self.tag = tag
    }

    func cell<C: UITableViewCell>() -> C {
        guard let cell = _cell as? C else {
            fatalError("cell 類型錯誤")
        }
        return cell
    }
    
    func cellItem<M>() -> M {
        guard let cellItem = viewData as? M else {
            fatalError("cellItem 類型錯誤")
        }
        return cellItem
    }

    private var _cell: Cell?

    func update(cell: UITableViewCell) {
        if let cell = cell as? Cell {
            self._cell = cell
            cell.update(viewData: viewData)
        }
    }
}

extension Row: RowType {}

public class RowTags {
    fileprivate init() {}
}

public class RowTag: RowTags {
    public let _key: String
    
    public init(_ key: String) {
        self._key = key
        super.init()
    }
}

extension RowTag: Hashable {
    public static func ==(lhs: RowTag, rhs: RowTag) -> Bool {
        return lhs.hashValue == rhs.hashValue
    }

    public var hashValue: Int {
        return _key.hashValue
    }
}

extension RowTags {
    static let none = RowTag("")
}
複製代碼

以上代碼,對比OC實現主要有三點不一樣:markdown

  • CellClass從參數變爲泛型
  • RowType的存在
  • RowTags(主要用來取Row,代替IndexPath)

不知道你對RowType這個協議的存在是否感到疑惑,假如沒有它行不行?app

若是沒有RowType這個協議,這兩個row應該放在什麼類型的數組裏呢?函數

你打算用Any?那你的代碼裏確定會出現一堆as? 的代碼,顯然與咱們談到的優雅背道而馳。oop

其實RowType的存在就是這些元素的抽象,讓咱們知道這些元素的共同屬性。

是否是典型的面相協議編程?post


泛型Any的區別

若是沒有怎麼接觸過Swift的同窗,或者不太瞭解泛型的同窗,看到上面的語法,確定是一臉的懵逼。

在這裏簡單給不太瞭解的同窗普及一下。

泛型,泛型,從字面理解就是普遍的類型嘛,就是各類姿式都知足,可是他和Any有什麼不一樣呢?

咱們先來看這麼一個需求,我想寫一個max函數,他要使用各類類型,若是沒有接觸過泛型的同窗寫出來的函數應該是這樣(請只看方法定義)

func anyMax(_ x: Any, _ y: Any) -> Any {
    ....
}
複製代碼

若是對於OC那樣指針操做的語言這彷佛沒有問題,可是這對於Swift這樣的強類型語言就頗有問題了。

爲何?

假如我比較兩個Int類型的數字,返回的是Any,這顯然不是我想要的

let n = 1
let m = 2
// result 的類型會爲Any
let result = anyMax(n, m)
複製代碼

再看泛型版本

func genericMax<T>(_ x: T, _ y: T) -> T {
    ....
}
複製代碼
let n = 1
let m = 2
// result 的類型會爲Int
let result = genericMax(n, m)
複製代碼

由於Swift有類型推斷,因此咱們在輸入值比較時就知道了咱們的result類型爲Int

那麼咱們能夠總結出泛型Any的最大區別就是:

  • 泛型

    • 輸入與輸出結果類型一致
    • 延遲類型的肯定
  • Any會形成類型丟失

Swiftmax函數的實現

where關鍵字表示約束條件,T必須爲遵循了協議Comparable的類型


在Swift中優雅的使用UITableView構建List頁面

再看一遍這張圖

這有三組樣式的UITableView

其實ListDetail維護的東西是同樣的,就是那個RowContainer

核心代碼

尾巴

在此OC和Swift的優雅使用UITabelView都已經和你們介紹完畢了。

下一節會和你們分享一下在我開發中,對Detail界面的運用和List界面的運用,以及怎麼用泛型去對Detail模型和List模型的解析。

在上一節中,有不少同窗給我推薦了一些表單的庫,其實我本身也知道有不少優秀的表單庫,列如EurekaXLForm等等。

那麼,我爲何還要本身造輪子?

兩個主要的緣由:

  • 那些庫都太
  • 都只支持Detail,List界面沒有辦法兼容

其實我也只是站在了大佬的肩上而已


在上一節中看到評論中主要有兩個問題:

  • 我用了響應鏈的傳值方式,怎麼傳遞參數?
func buttonAction(_ sender: UIButton) {
        (self.viewController as? ButtonCellActionable)?.buttonAction(sender, cell: self)
    }
複製代碼
  • 在執行懶加載UIbutton時,self.viewController爲nil,爲何事件還能相應?

其實爲nil,在我開發時,我是知道的,但我錯誤的理解爲,系統會在運行時再去拿那個target。

爲nil的緣由實際上是button尚未添加在superView上,響應鏈還找不到他的UIViewContoller。

那麼既然,target沒有被系統持有,那麼,爲何事件還能相應?

這就是UIKit中的定義,就是target爲nil的時候,會走相應鏈 ,而我以前的實現,又剛好在VC中實現了,因此方法會被調用。

關於addTarget這個方法的更多事情,請看這裏

Demo

表單庫:

相關文章
相關標籤/搜索