TableView中重用TextField的问题

在开发项目中,经常会遇到多输入框页面,如果输入框样式一致或者差异较小,就可以采用tableView来实现。但是在tableView中重用textField会产生两个问题:一个是键盘弹出后textField怎样保持在键盘上方,另一个是textField滑动后会丢失输入数据或者输入数据错乱。其实还有一个附加问题:在使用tableView的页面如何收起键盘?

问题1:键盘弹起后,textField怎样才能保持在键盘上方呢,一般的方式是通过监听键盘弹出和收起的通知来改变tableViewcontentOffset,一个更简单的方式是:

1
2
3
4
5
6
7
8
9
10
11
12
table = UITableView(frame: view.bounds, style: .plain)
// 把UITableViewController作为self的childViewController,并把self.table指向tableViewController的tableView
let tableController = UITableViewController(style: .plain)
self.addChild(tableController)
table = tableController.tableView
///tableView的其他设置
table.register(UINib.init(nibName: "MyTextFieldCell", bundle: nil), forCellReuseIdentifier: "\(MyTextFieldCell.self)")
table.delegate = self
table.dataSource = self
table.rowHeight = 60
table.tableFooterView = UIView()
view.addSubview(table)

这样做是因为UITableViewController已经为我们做好了键盘弹出收起时cell位置的变化处理。

问题2:textField滑动后会丢失输入数据或者输入数据错乱

要解决cell重用后输入数据错乱问题,我们需要对数据进行合理的保存。在cell中实现UITextFieldDelegate的代理方法,在textFieldDidEndEditing方法中调用cell的闭包saveInput(textField.text),将TextField的text内容传出去,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 声明一个传出输入框内容的闭包
var saveInput:((String?)->())?

extension MyTextFieldCell:UITextFieldDelegate{
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
if let callback = clickTextFieldBlock {
callback(textField.frame)
}
return true
}

func textFieldDidEndEditing(_ textField: UITextField) {
if let save = saveInput {
save(textField.text)//传出TextField的输入内容
}
}

func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
}

在controller中,使用infoModel变量来保存各输入框的输入内容,在cellForRow时实现cell的闭包saveInput保存数据到InfoModel,并从中取出数据给TextField展示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// infoModel
var infoModel:[IndexPath:String] = [:]

// cellForRow
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "\(MyTextFieldCell.self)", for: indexPath) as! MyTextFieldCell
cell.selectionStyle = .none
// 保存输入框内容到infoModel
cell.saveInput = { [weak self] (info) in
self?.infoModel[indexPath] = info
}
// 取出相应的输入框内容展示给TextField
cell.textField.text = self.infoModel[indexPath]
return cell
}

最后,在一般的viewController中,要结束输入收起键盘,调用view.endEditing(true)即可,在tableView中由于tableView响应了touchBegan的手势,所以view.endEditing(true)没有作用,这时候就需要在TextField的代理方法上来处理了,在用户点击return的时候使TextField失去第一响应即可:

1
2
3
4
func textFieldShouldReturn(_ textField: UITextField) -> Bool {	
textField.resignFirstResponder()
return true
}