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

Table View Controller中已调度通知列表显示异常求助

问题诊断与解决方案

我一眼就看出问题出在异步操作的时机把控和UI更新的线程正确性上,这也是iOS开发中处理异步数据加载时常见的坑!

问题根源

  1. 异步方法的执行顺序问题:UNUserNotificationCenter.getPendingNotificationRequests是一个异步方法,它的回调会在后台线程执行。你在viewWillAppear里调用generateReminders()后立刻调用tableView.reloadData(),这时候回调还没执行,reminders数组还是空的,所以tableView一开始显示不出内容。
  2. UI更新未在主线程执行:你在后台线程的回调里修改了reminders和remindersCount,但后续的UI刷新没有切回主线程,虽然数组操作本身线程安全,但tableView的刷新必须在主线程完成,否则会导致显示不稳定。
  3. 数据一致性问题:cellForRowAt里依赖remindersCount判断索引,而remindersCount是在回调里赋值的,这可能导致数组长度和count值不一致,进一步加剧显示异常。

修复步骤

1. 修正generateReminders方法,确保主线程更新UI

把数据处理后的tableView刷新操作放到主线程,并且先清空旧数据避免重复:

func generateReminders() {
    // 先清空旧数据,防止重复添加
    reminders.removeAll()
    
    let center = UNUserNotificationCenter.current()
    center.getPendingNotificationRequests { [weak self] notifications in
        guard let self = self else { return }
        
        for item in notifications {
            if let trigger = item.trigger as? UNCalendarNotificationTrigger, let triggerDate = trigger.nextTriggerDate() {
                // 简化withSound的判断逻辑
                let withSound = item.content.sound == UNNotificationSound.default
                let reminder = Reminder(
                    identifier: item.identifier,
                    time: triggerDate,
                    message: item.content.body,
                    withSound: withSound,
                    isAPendingNotification: true
                )
                self.reminders.append(reminder)
            }
        }
        self.remindersCount = notifications.count
        
        // 必须回到主线程刷新UI
        DispatchQueue.main.async {
            self.tableView.reloadData()
        }
    }
}

2. 调整viewWillAppear,移除提前的reloadData

现在刷新操作已经放在异步回调的主线程里了,不需要在viewWillAppear里手动调用:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    generateReminders()
    // 移除这里的tableView.reloadData(),等待数据准备好后再刷新
}

3. 优化cellForRowAt的逻辑,保证数据一致性

直接用reminders.count判断索引,避免依赖单独的remindersCount变量:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "Reminder", for: indexPath)
    
    // 先判断索引是否在数组范围内
    guard indexPath.row < reminders.count else {
        cell.textLabel?.text = ""
        cell.detailTextLabel?.text = ""
        return cell
    }
    
    let reminder = reminders[indexPath.row]
    let formatter = DateFormatter()
    formatter.dateFormat = "HH:mm"
    cell.textLabel?.text = formatter.string(from: reminder.time) // 建议Swift属性用小驼峰命名,Time改成time更规范
    cell.detailTextLabel?.text = reminder.message
    
    return cell
}

4. 简化viewWillDisappear方法

tableView.setNeedsDisplay()是多余的,清空数据即可:

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    reminders.removeAll()
    remindersCount = 0
}

为什么这样能解决问题?

  • 现在数据加载完成后才会触发tableView刷新,保证刷新时reminders数组已经有完整数据。
  • 所有UI操作都在主线程执行,符合iOS的UI线程安全要求。
  • 移除了冗余的remindersCount依赖,避免数据不一致导致的显示异常。

内容的提问来源于stack exchange,提问作者Alex Marchant

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 09:27:15