UITableView插入记录不显示、删除异常问题求助及代码修复
嘿,我来帮你搞定这两个头疼的问题!咱们先逐个拆解原因,再给出具体的修复方案~
问题1:插入新联系人后,UITableView死活不显示
为啥会这样?
你看哈,你把新联系人加到了contactsArray里,但UITableView渲染时用的是sections数组——可你的sections一开始是空的,也没把contactsArray的数据同步过去,表格找不到数据,当然显示不出来啦!
而且你代码里的sections是二维数组,看起来是想做分组,但完全没初始化逻辑,相当于数据源“分家”了,一边加数据一边用另一个空数组渲染,肯定不行。
怎么修复?
如果你的需求是单组联系人列表(就像普通通讯录那样),咱们可以统一数据源,要么直接用contactsArray,要么让sections和contactsArray保持同步。这里我推荐用sections(以后要加分组功能也方便):
- 先在
viewDidLoad里初始化sections,把contactsArray作为第一组:
override func viewDidLoad() { super.viewDidLoad() contactsTableView.delegate = self contactsTableView.dataSource = self phoneTextField.delegate = self // 把contactsArray放进sections当第一组 sections = [contactsArray] }
- 修改插入按钮的逻辑,把新联系人加到
sections的第一组里,同时同步更新contactsArray,还用动画插入行(比直接刷新整个表格流畅多了):
@IBAction func insertNewContact(_ sender: UIButton) { // 先判断名字输入框有没有内容 guard let name = nameTextField.text, !name.isEmpty else { return } let newContact = Contact(fullName: name, phoneNumber: phoneTextField.text ?? "") // 加到sections的第一组 sections[0].append(newContact) // 同步更新contactsArray(如果其他地方还要用它的话) contactsArray.append(newContact) // 计算新行的位置,用动画插入 let newIndexPath = IndexPath(row: sections[0].count - 1, section: 0) contactsTableView.insertRows(at: [newIndexPath], with: .automatic) // 顺便清空输入框,用户体验更好 nameTextField.text = "" phoneTextField.text = "" }
问题2:删除时删了整个section,不是选中的行
问题出在哪?
你删除的时候写了sections.remove(at: indexPath.row)——sections是二维数组啊!indexPath.row是当前行的序号,indexPath.section才是组的序号。你这相当于直接删掉了整个组,而不是组里的某一行,难怪会删错!
而且直接调用reloadData会丢失删除动画,咱们用UITableView自带的批量更新方法更合适。
修复方案
修改删除的逻辑,先找到对应的组,再删掉组里的指定行,然后用动画更新表格:
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) { if editingStyle == .delete{ // 先找到当前组,删掉里面的指定行 sections[indexPath.section].remove(at: indexPath.row) // 同步更新contactsArray(单组情况下直接用indexPath.row就行) contactsArray.remove(at: indexPath.row) // 用动画删除该行,比刷新整个表格舒服 tableView.deleteRows(at: [indexPath], with: .automatic) } }
要是以后想做多组联系人,那contactsArray的全局索引得这么算:
// 把前面所有组的行数加起来,再加上当前行的row,就是全局索引 let globalIndex = sections[0..<indexPath.section].reduce(0) { $0 + $1.count } + indexPath.row contactsArray.remove(at: globalIndex)
完整修复后的代码
把上面的修改整合后,完整代码是这样的:
class Contact { var fullName: String var phoneNumber: String? init(fullName: String, phoneNumber: String) { self.fullName = fullName self.phoneNumber = phoneNumber } } var contactsArray = [Contact]() var sections = [[Contact]]() // Logic functionality for my "Contact" application class ContactViewController: UIViewController, UITextFieldDelegate { @IBOutlet var nameTextField: UITextField! @IBOutlet var phoneTextField: UITextField! @IBOutlet var contactsTableView: UITableView! override func viewDidLoad() { super.viewDidLoad() contactsTableView.delegate = self contactsTableView.dataSource = self phoneTextField.delegate = self // 初始化sections,将contactsArray作为第一组 sections = [contactsArray] } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) contactsTableView.reloadData() } } extension ContactViewController: UITableViewDelegate, UITableViewDataSource{ // Add a new contact to the end of list @IBAction func insertNewContact(_ sender: UIButton) { guard let name = nameTextField.text, !name.isEmpty else { return } let newContact = Contact(fullName: name, phoneNumber: phoneTextField.text ?? "") // 添加到sections的第一组 sections[0].append(newContact) // 同步更新contactsArray contactsArray.append(newContact) // 插入行并显示动画 let newIndexPath = IndexPath(row: sections[0].count - 1, section: 0) contactsTableView.insertRows(at: [newIndexPath], with: .automatic) // 清空输入框 nameTextField.text = "" phoneTextField.text = "" } // Return no of sections from your list func numberOfSections(in tableView: UITableView) -> Int { return sections.count } // Return no of rows in each section from your list func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return sections[section].count } // Insert a custom cell in your table view func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let contact = sections[indexPath.section][indexPath.row] let cell = tableView.dequeueReusableCell(withIdentifier: "CustomTableViewCell", for: indexPath) as! CustomTableViewCell cell.configContact(contact) return cell } // Delete a row when you swipe from right to left func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) { if editingStyle == .delete{ // 删除对应组里的指定行 sections[indexPath.section].remove(at: indexPath.row) // 同步更新contactsArray(单组情况下直接用indexPath.row) contactsArray.remove(at: indexPath.row) // 动画删除行 tableView.deleteRows(at: [indexPath], with: .automatic) } } }
额外小 tips
- 用
guard let替代强制解包,代码更安全,不会轻易崩溃; - 插入后清空输入框,用户不用自己删,体验更好;
- 尽量用UITableView的动画更新方法(
insertRows/deleteRows),比直接reloadData更流畅,视觉效果也更好; - 如果不需要分组功能,其实可以直接删掉
sections数组,全部用contactsArray作为数据源,逻辑会更简单哦~
内容的提问来源于stack exchange,提问作者Florentin Lupascu
相关产品推荐
相关产品推荐

