使用TPKeyboardAvoidingTableView时,无法设置TextField代理如何获取单元格内值?
解决TPKeyboardAvoidingTableView中单元格TextField的值获取问题
嘿,我来给你几个实用的方案搞定这个问题——毕竟用TPKeyboardAvoidingTableView处理键盘遮挡时,cell内TextField的代理绑定确实容易碰到小卡点,其实换几种思路就能轻松拿到输入值啦:
方案一:自定义Cell+闭包回调(最推荐,解耦性好)
这种方法把TextField的代理逻辑封装在自定义Cell内部,通过闭包把值传递给ViewController,完全避开代理绑定的问题:
- 先自定义带TextField的Cell
class CustomTextFieldCell: UITableViewCell, UITextFieldDelegate { // 可以在xib/storyboard拖入TextField,也可以代码创建 let inputTextField = UITextField() // 定义闭包,用来回调文本变化 var textChangedHandler: ((String?) -> Void)? override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) setupTextField() } required init?(coder: NSCoder) { super.init(coder: coder) setupTextField() } private func setupTextField() { // 简单布局TextField,可根据需求调整 inputTextField.frame = contentView.bounds.inset(by: UIEdgeInsets(top: 8, left: 16, bottom: 8, right: 16)) inputTextField.borderStyle = .roundedRect contentView.addSubview(inputTextField) // 给TextField设置代理 inputTextField.delegate = self // 监听文本变化事件 inputTextField.addTarget(self, action: #selector(textFieldDidChange), for: .editingChanged) } @objc private func textFieldDidChange() { // 调用闭包传递当前文本 textChangedHandler?(inputTextField.text) } // 可选:处理回车收起键盘的逻辑 func textFieldShouldReturn(_ textField: UITextField) -> Bool { textField.resignFirstResponder() return true } }
- 在ViewController中绑定闭包接收值
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "CustomTextFieldCell", for: indexPath) as! CustomTextFieldCell // 设置TextField初始值 cell.inputTextField.text = yourDataSourceArray[indexPath.row] // 绑定闭包,用weak self避免循环引用 cell.textChangedHandler = { [weak self] newText in guard let self = self else { return } // 更新数据源数组 self.yourDataSourceArray[indexPath.row] = newText ?? "" // 也可以在这里做实时输入验证等操作 } return cell }
方案二:利用NotificationCenter监听文本变化
如果不想自定义Cell,也可以通过系统通知监听所有TextField的文本变化,再定位到对应的cell和indexPath:
- 注册通知
在ViewController的viewDidLoad中添加:
override func viewDidLoad() { super.viewDidLoad() // 监听TextField文本变化通知 NotificationCenter.default.addObserver(self, selector: #selector(textFieldTextDidChange(_:)), name: UITextField.textDidChangeNotification, object: nil) }
- 处理通知回调
@objc private func textFieldTextDidChange(_ notification: Notification) { guard let textField = notification.object as? UITextField, let cell = textField.findSuperview(ofType: UITableViewCell.self), let indexPath = tableView.indexPath(for: cell) else { return } // 更新数据源 yourDataSourceArray[indexPath.row] = textField.text ?? "" } // 给UIView加扩展,方便查找父级Cell extension UIView { func findSuperview<T: UIView>(ofType type: T.Type) -> T? { var currentView = superview while currentView != nil { if let targetView = currentView as? T { return targetView } currentView = currentView?.superview } return nil } }
- 记得移除通知
deinit { NotificationCenter.default.removeObserver(self) }
方案三:给TextField添加Target-Action
和通知类似,但更直接给每个TextField绑定事件:
在cellForRowAt方法中:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "YourCellIdentifier", for: indexPath) guard let textField = cell.viewWithTag(100) as? UITextField else { return cell } // 假设TextField的tag设为100 textField.text = yourDataSourceArray[indexPath.row] // 先移除旧的target,避免Cell复用重复绑定 textField.removeTarget(nil, action: nil, for: .allEvents) // 添加编辑变化事件 textField.addTarget(self, action: #selector(textFieldEditingChanged(_:)), for: .editingChanged) return cell } @objc private func textFieldEditingChanged(_ sender: UITextField) { guard let cell = sender.findSuperview(ofType: UITableViewCell.self), let indexPath = tableView.indexPath(for: cell) else { return } yourDataSourceArray[indexPath.row] = sender.text ?? "" }
小提示
- 不管用哪种方法,都要注意Cell复用的问题:比如给TextField设置初始值、移除旧的target/闭包,避免出现数据错乱。
- TPKeyboardAvoidingTableView本质是UITableView的子类,这些方法和普通TableView的处理逻辑完全兼容,不用担心冲突。
内容的提问来源于stack exchange,提问作者shakshi
相关产品推荐
相关产品推荐

