You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

UITableView插入记录不显示、删除异常问题求助及代码修复

嘿,我来帮你搞定这两个头疼的问题!咱们先逐个拆解原因,再给出具体的修复方案~


问题1:插入新联系人后,UITableView死活不显示

为啥会这样?

你看哈,你把新联系人加到了contactsArray里,但UITableView渲染时用的是sections数组——可你的sections一开始是空的,也没把contactsArray的数据同步过去,表格找不到数据,当然显示不出来啦!

而且你代码里的sections是二维数组,看起来是想做分组,但完全没初始化逻辑,相当于数据源“分家”了,一边加数据一边用另一个空数组渲染,肯定不行。

怎么修复?

如果你的需求是单组联系人列表(就像普通通讯录那样),咱们可以统一数据源,要么直接用contactsArray,要么让sections和contactsArray保持同步。这里我推荐用sections(以后要加分组功能也方便):

  1. 先在viewDidLoad里初始化sections,把contactsArray作为第一组:
override func viewDidLoad() {
    super.viewDidLoad()
    contactsTableView.delegate = self
    contactsTableView.dataSource = self
    phoneTextField.delegate = self
    // 把contactsArray放进sections当第一组
    sections = [contactsArray]
}
  1. 修改插入按钮的逻辑,把新联系人加到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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:43:38