如何实现按下回车键时在UITableView的textview间切换激活状态
问题原因
- 仅实现了列表滚动,没有手动切换输入响应者:当前输入焦点仍停留在原来的textView上,所以新输入的内容都会写到第0行的textView中
- 调用
tableView.reloadData()会刷新所有可见cell,打断响应者链,也会导致刚滚动出来的cell被重建,无法正常激活 - 写死跳转
row=1不支持连续跳转,也没有做边界判断
修复方案
第一步:新增获取textView对应行号的便捷方法
private func indexPathForTextView(_ textView: UITextView) -> IndexPath? { let point = textView.convert(textView.bounds.origin, to: tableView) return tableView.indexPathForRow(at: point) }
第二步:修改textViewDidChange逻辑
func textViewDidChange(_ textView: UITextView) { // 检测换行输入 guard let newLineRange = textView.text.range(of: "\n") else { return } // 移除多余的换行符 textView.text.removeSubrange(newLineRange) // 获取当前行、计算下一行,做越界判断 guard let currentIndex = indexPathForTextView(textView), currentIndex.row + 1 < pageCount else { return } let nextIndexPath = IndexPath(row: currentIndex.row + 1, section: 0) // 释放当前textView的输入焦点 textView.resignFirstResponder() // 滚动到下一行 tableView.scrollToRow(at: nextIndexPath, at: .top, animated: true) // 等待滚动和cell渲染完成后,激活下一行的textView DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) { [weak self] in guard let self = self, let nextCell = self.tableView.cellForRow(at: nextIndexPath) as? EditorTableViewCell else { return } nextCell.textView.becomeFirstResponder() } }
注意事项
- 不要在滚动逻辑中调用
reloadData,如果需要持久化每行的文本内容,单独维护一个[String]类型的数组存储每行文本,在cellForRowAt中给cell.textView赋值,在textViewDidChange中同步更新对应索引的数组元素即可 - 如果追求更丝滑的体验,可以实现
UIScrollViewDelegate的scrollViewDidEndScrollingAnimation方法,在滚动结束后再触发下一个textView的激活,不需要硬编码延迟时间 - 记得在
EditorTableViewCell的prepareForReuse方法中清空textView的代理和内容,避免复用导致的异常
内容的提问来源于stack exchange,提问作者NewUser
相关产品推荐
相关产品推荐

