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

如何解决Swift中UITableView向下滚动时cell数据异常刷新问题

问题根因

这是UITableView Cell复用机制导致的典型数据错乱,你的代码有两个核心错误:

  1. 没有单独存储每一条高光记录的快照数据,所有Cell渲染时都读取当前计时器、比分栏、事件输入框的实时值
  2. Cell滚出屏幕后会被系统回收进入复用池,当它再次被加载到屏幕上时,cellForRowAt会重新执行,此时读到的就是最新的UI值,自然会覆盖原来记录的历史内容
    另外你把设置Gamelog.backgroundColor的逻辑写在numberOfRowsInSection里是错误写法,这个方法会被系统频繁调用,只应该用来返回对应分区的行数,不要放任何UI配置代码。
修复方案

核心原则:UITableView的所有行内容必须由独立的数据源数组驱动,永远不要在cellForRowAt里读取和当前行无关的全局变量、实时UI控件值。

步骤1:定义单条记录的数据模型

用来保存每一次高光生成瞬间的所有信息,生成后就不会再变动:

struct GameLogRecord {
    let time: String
    let scoreA: String
    let scoreB: String
    let eventContent: String
}

步骤2:用数组作为唯一数据源

替换你原来零散的NumberofScores、Gamelogtext全局变量,用数组存储所有历史记录:

// 你的ViewController里的属性
var gameRecords: [GameLogRecord] = []
let cellReuseIdentifier = "cell"

把原来写在numberOfRowsInSection里的UI配置移到viewDidLoad里执行:

override func viewDidLoad() {
    super.viewDidLoad()
    Gamelog.backgroundColor = .clear
    // 如果你是用Storyboard拖的Cell可以省略下面这行注册代码
    Gamelog.register(UITableViewCell.self, forCellReuseIdentifier: cellReuseIdentifier)
}

步骤3:重写TableView数据源方法

cellForRowAt里只从数据源数组取对应行的数据渲染,不读任何实时UI值:

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return gameRecords.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    // 安全的Cell复用写法
    guard let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) else {
        return UITableViewCell(style: .default, reuseIdentifier: cellReuseIdentifier)
    }
    // 取当前行对应的历史记录
    let record = gameRecords[indexPath.row]
    cell.textLabel?.text = "\(record.time) Score: \(record.scoreA)-\(record.scoreB) - \(record.eventContent)"
    cell.textLabel?.adjustsFontSizeToFitWidth = true
    cell.backgroundColor = UIColor(red: 0/255, green: 144/255, blue: 81/255, alpha: 1)
    cell.textLabel?.textColor = .white
    return cell
}

步骤4:修改按钮点击的新增逻辑

点击按钮的瞬间就把当时的时间、比分、事件内容存成快照加入数据源,不要用单个全局变量临时存文本:

// 按钮点击触发新增记录
func addRecordButtonTapped() {
    // 生成记录快照,此时读取的UI值就是点击瞬间的值,后续UI变化不会影响这条记录
    let newRecord = GameLogRecord(
        time: TimerLabel.text ?? "",
        scoreA: AScore.text ?? "0",
        scoreB: BScore.text ?? "0",
        eventContent: "\(TeamA.text ?? "") TRY \(A1.text ?? "")"
    )
    // 必须先更新数据源,再操作TableView插入行
    gameRecords.append(newRecord)
    Gamelog.performBatchUpdates({
        Gamelog.insertRows(at: [IndexPath(row: gameRecords.count - 1, section: 0)], with: .automatic)
    }, completion: nil)
}

补充提示:UITableView的Cell复用是系统的内存优化机制,不会保留屏幕外的Cell内容,所有和行绑定的内容必须存在独立的数据源中,这是iOS开发使用TableView的基础规则。


内容的提问来源于stack exchange,提问作者Jack Martin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 13:06:24