表驱动法在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
。结合ReactiveCocoa
把didSelectRow
的代码写在cellForRow
里,可以方便的写出复杂的填表页面。
具体代码,大家可以参考 Form