表驱动法在iOS中的应用

2017-02-09  本文已影响115人  weithl

表驱动法(Table-Driven Methods)出自《代码大全》。它是一种编程模式——从表里面查找信息而不使用逻辑语句。

表提供了一种负责的逻辑和继承结构的替换方案。如果你发现自己对某个应用程序的逻辑或继承树关系感到困惑,那么问问自己它是否可以通过一个查询表来加以简化。

在实际使用中,我们常常在cellForRow中写上了很多的逻辑判断语句。在大多数情况下,可以通过表驱动法更便捷地达到目的。

最简单的例子:
初学 iOS 时,想必大家都写过下面类似的代码:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier")! 
        if indexPath.row == 0 {
            cell.textLabel?.text = "title0"
        } else if indexPath.row == 1 {
            cell.textLabel?.text = "title1"
        } else if indexPath.row == 2 {
            cell.textLabel?.text = "title2"
        }

    return cell
}

根据表驱动法的思路,可以用以下方式替代:

var contents: [String] = []
    override func viewDidLoad() {
        super.viewDidLoad()
        contents = ["title0", "title1", "title2"]
    }

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier")!
        let title = contents[indexPath.row]
        cell.textLabel?.text = title

    return cell
}

当然,实际情况不可能这么简单。比如要创建一个简单的设置界面,这个时候contents可以包含字典以设置cell属性。
例如:

var contents:[[String: Any]] = []
override func viewDidLoad() {
    super.viewDidLoad()
    tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cellid")
        
    for x in 0 ..< 3 {
       let dict = [
            "id" : "cellid",
            "title" : "title" + String(x),
            "detail" : "detail" + String(x),
            "height" : 44.0
        ] as [String : Any]
        contents.append(dict)
    }
    
}


override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return contents.count
}

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    let dict = contents[indexPath.row] as [String : Any]
    let height = dict["height"] as? Double
    return CGFloat(height!)
    
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "cellid")!
    
    let dict = contents[indexPath.row] as [String : Any]
    let title = dict["title"] as? String

    cell.textLabel?.text = title
    
    return cell
}

这个时候就不需要同时在 cellForRow heightForRow 进行判断去设置属性了。

进一步,可以将上例中的字典替存储在本地文件(json或者plist,二者没有明显的性能差距)。简单的json文件示例:

{
     "applyInfo" : [
        {
            "header" : "",
            "height" : 0.0001,
            "footer" : "",
            "footerHeight" : 20,
            "content" : [
                {
                    "title" : "借款编号",
                    "height" : 50,
                    "detail" : "",
                    "id" : "someID"
                },
                {
                    "title" : "产品类型",
                    "height" : 50,
                    "detail" : "",
                    "id" : "someID"
                }
            ]
        },
        {
            "header" : "",
            "height" : 50,
            "footer" : "",
            "footerHeight" : 0.0001,
            "content" : [
                {
                    "title" : "姓名",
                    "height" : 50,
                    "detail" : "",
                    "id" : "someID"
                },
                {
                    "title" : "性别",
                    "height" : 50,
                    "detail" : "",
                    "id" : "someID"
                },
            ]
        },
    ]
}

每次取出时,转换成cellModel对象。如果有多个Section,再增加一层sectionModel
通过这种方式,封装成一个基类FormController。结合ReactiveCocoadidSelectRow的代码写在cellForRow里,可以方便的写出复杂的填表页面。

具体代码,大家可以参考 Form

上一篇 下一篇

猜你喜欢

热点阅读