UITableView 中的 UITextField 没有成为第一响应者
Posted
技术标签:
【中文标题】UITableView 中的 UITextField 没有成为第一响应者【英文标题】:UITextField in UITableView not becoming first responder 【发布时间】:2017-06-22 19:27:46 【问题描述】:我有UITableView
,大约有 20 行,每行包含一个UITextField
。我第一次点击文本字段时将打开键盘,我准备编辑此文本字段。如果我点击下一个文本字段(注意键盘一直显示),键盘仍然显示,但蓝色光标不在新文本字段中,我无法输入任何文本。但是,如果我再次点击另一个文本字段,它就可以正常工作。这种行为交替发生,一次有效,另一次无效。
委托方法textFieldShouldBeginEditing(_:)
总是被调用,不管我是否可以编辑。委托方法textFieldDidBeginEditing(_:)
仅在编辑工作时调用。
这是cellForRowAt
的代码
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
let cell = tableView.dequeueReusableCell(withIdentifier: "TextFieldCell")!
let titleLabel = cell.viewWithTag(1) as! UILabel
let contentTextField = cell.viewWithTag(2) as! FixableTextField
contentTextField.delegate = self
contentTextField.inputAccessoryView = doneToolbar
// Enable/disable editing for text fields
if isEditing
contentTextField.enableEditing()
else
contentTextField.disableEditing()
// Present Profile Data
if profileUpdateBuffer != nil
switch indexPath.row
case 0:
titleLabel.text = "Count"
contentTextField.text = "\(profileUpdateBuffer!.count)"
contentTextField.purposeID = "count"
contentTextField.keyboardType = .numberPad
case 1:
titleLabel.text = "City"
contentTextField.text = "\(profileUpdateBuffer!.city)"
contentTextField.purposeID = "city"
contentTextField.keyboardType = .default
// ...
case 20:
titleLabel.text = "Name"
contentTextField.text = "\(profileUpdateBuffer!.name)"
contentTextField.purposeID = "name"
contentTextField.keyboardType = .default
default:
titleLabel.text = ""
contentTextField.text = ""
return cell
// No data available -> show info in first row
else
if indexPath.row == 0
titleLabel.text = "No data"
contentTextField.text = "No data"
else
titleLabel.text = ""
contentTextField.text = ""
return cell
enableEditing()
和 disableEditing()
方法来自类 FixableTextField
。我可以看到文本字段始终处于启用状态,因为我可以看到文本字段边框
// Extract from FixableTextField class
func enableEditing()
self.isEnabled = true
self.borderStyle = .roundedRect
func disableEditing()
self.isEnabled = false
self.borderStyle = .none
UITextField
的代码
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool
// Delete empty field indicator "-"
if textField.text == "-"
textField.text = ""
//Move profileTable's contentView to correct position
if textField is FixableTextField
let path = IndexPath(row: rowMap[(textField as! FixableTextField).purposeID]!, section: 0)
moveContentViewUp(indexPath: path)
return true
func textFieldDidEndEditing(_ textField: UITextField)
// Save new value to profileUpdateBuffer
do
try self.profileUpdateBuffer?.setProperty(value: textField.text!, key: (textField as! FixableTextField).purposeID)
catch ProfileError.PropertySettingWrongType
let falseInputAlert = UIAlertController(title: "False Input", message: "The input for this field is not valid.", preferredStyle: .alert)
falseInputAlert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil))
self.present(falseInputAlert, animated: true, completion: nil)
catch
print("Error when trying to set property for profileUpdateBuffer in ProfileViewController")
// Display new data in table
profileTable.reloadData()
从类ProfileData
中的setProperty
方法中提取。 profileUpdateBuffer
的类型为 ProfileData
func setProperty(value:String, key:String) throws
switch key
case "count":
count = value
case "city":
count = value
// ...
case "name":
name = value
default:
throw ProfileError.PropertySettingWrongType
【问题讨论】:
请发布您的代码,以便我们为您提供帮助。特别是您的 cellForRowAt indexPath 函数。如何将侦听器添加到您的文本字段? 谢谢,我当然会的。 'textFieldShouldBeginEditing(_:)'中是否有过滤逻辑?我看到您还将您的 VC 设置为所有文本字段的代表。您是否有逻辑来区分委托方法实现中的单元格?您可以尝试在单元格中移动 TextField 委托,以便每个单元格自己负责。 您是否总是在您的textFieldShouldBeginEditing
中返回true
。如果没有,你能发布代码吗?
想象一下这种情况:您有单元格 1 和单元格 2,它们各自的文本字段 1 (tf1) 和文本字段 2 (tf2)。您正在编辑 tf1.然后选择tf2。该框架将辞去 tf1 上的第一响应者并成为 tf2 上的第一响应者。这将触发 tf2 的 shouldBeginEditing。现在,响应 tf1 上的已辞职响应者,将使用 didEndEditing 调用 tf1 的代表。但是您正在编辑 tf2,而不是 tf1!此调用将有效地使 tf2 不可编辑。您可以在代码中添加一些日志并确认这是真的吗?如果为真,请尝试通过在单元格中移动 tf 委托来解决此问题。
【参考方案1】:
我制作了一个小程序来模仿您描述的行为。
看来问题是由textFieldDidEndEditing(_:)
末尾的表格视图数据重新加载引起的:
func textFieldDidEndEditing(_ textField: UITextField)
// Save new value to profileUpdateBuffer
do
try self.profileUpdateBuffer?.setProperty(value: textField.text!, key: (textField as! FixableTextField).purposeID)
catch ProfileError.PropertySettingWrongType
let falseInputAlert = UIAlertController(title: "False Input", message: "The input for this field is not valid.", preferredStyle: .alert)
falseInputAlert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil))
self.present(falseInputAlert, animated: true, completion: nil)
catch
print("Error when trying to set property for profileUpdateBuffer in ProfileViewController")
// Display new data in table
profileTable.reloadData()
尝试删除profileTable.reloadData()
,以便进行实验以确认问题的根本原因(是的,您的其他单元格将不会更新)。
解决此问题的一种方法是在 textFieldDidEndEditing(_:)
中使用 visibleCells
上的直接单元格更新。我看到profileUpdateBuffer?
是您的数据模型。如果它们位于表格视图的可见单元格属性中,只需从模型中手动更新单元格的 titleLabel 和 textField 属性即可。
如果您想相应地调整单元格的大小,请使用 AutoLayout 和 UITableViewAutomaticDimension
作为表格视图行高并结合 beginUpdates()
/endUpdates()
调用。
有关如何在不丢失键盘焦点的情况下实现直接单元格操作和/或动态单元格大小更新的更多详细信息check the accepted answer on this question我已经回答了。
希望这会有所帮助!
【讨论】:
非常感谢您的详细回答。你是对的,当我删除profileTable.reloadData()
时它工作得很好。正如你所说,我通过更新没有realodData
的单元格来修复它,现在一切正常。您是否解释了为什么在使用 reloadData
时会发生这种行为?
当您启动reloadData()
时,表格视图会自行禁用用户交互。然后,单元格将为 becomeFirstResponder()
返回 false。在您的情况下,第二个单元格不会成为第一响应者。如果您随后选择一个新单元格,则不会调用 reloadData()
,因为前一个单元格没有成为第一响应者,因此不会调用 didEndEditing()(重新加载所在的位置)。这就是可以编辑第三个单元格的原因。我不知道为什么当编辑不可用时键盘仍然在屏幕上 - 我希望当之前的文本字段退出第一响应者时它会被关闭。
完美的解释。非常感谢!【参考方案2】:
我认为问题在于您调用 profileTable.reloadData()... 您可能应该只重新加载一个单元格。或许 textFieldDidEndEditing(:) 被调用时使用旧的 TextField 作为参数,然后是新的 TextField 的 textFieldShouldBeginEditing(:)。问题是您刷新了 textFieldDidEndEditing(:) 末尾的所有单元格,这意味着系统作为参数传递给 textFieldShouldBeginEditing(:) 的 TextField 可能与相应的单元格包含...可能是这些新的击键被发送到属于可重用单元格队列中的单元格的 TextField,即不可见,但仍存在于内存中的某处。
【讨论】:
以上是关于UITableView 中的 UITextField 没有成为第一响应者的主要内容,如果未能解决你的问题,请参考以下文章
水平 UITableView 中的垂直 UITableView
在 UITableView 中的 UINib 中重新加载 UITableView
尝试在用户向 iOS 中的 UITableView 添加行时动态增加 UITableView 的高度