使用Timer更新UITableView时删除行操作受阻的问题
解决方案
方案1:只刷新可见行,避免全局重载
你当前每次Timer触发都调用reloadData(),会强制整个列表重新渲染,直接打断删除动画。改成只刷新当前可见的行,既不影响用户看到的时间更新,也不会干扰删除操作:
修改Timer闭包的最后一行:
// 替换原来的 citiesTableView.reloadData() let visibleRows = citiesTableView.indexPathsForVisibleRows ?? [] citiesTableView.reloadRows(at: visibleRows, with: .none)
方案2:编辑状态下暂停Timer的UI更新
加一个状态标记,在删除动画执行期间,让Timer跳过tableView刷新操作,动画结束后恢复:
- 先在类里添加一个私有标记:
private var isPerformingEdit = false
- 修改删除代理方法,利用
CATransaction监听动画结束:
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { if editingStyle == .delete { isPerformingEdit = true cities.remove(at: indexPath.row) // 用CATransaction捕获动画完成事件 CATransaction.begin() citiesTableView.deleteRows(at: [indexPath], with: .automatic) CATransaction.setCompletionBlock { self.isPerformingEdit = false } CATransaction.commit() } }
- 在Timer闭包里先判断状态:
clockTimer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { [self] t in // 编辑状态下跳过更新 guard !isPerformingEdit else { return } let myTime = Date().formatted(date: .omitted, time: .standard) for i in 0 ..< cities.count { let cityGMTDiff = cities[i].gmtDiff let overallGMTDiff = cityGMTDiff - myGMTDiff let cityTime = Date().addingTimeInterval(Double(overallGMTDiff) * 3600.0).formatted(date: .omitted, time: .standard) cities[i].time = cityTime } citiesTableView.reloadData() }
方案3:用计算属性动态获取时间,避免修改数据源
把城市的时间改成模型的计算属性,不需要每次Timer触发都修改数据源,只需要刷新可见行即可,从根源减少数据冲突:
- 修改你的City模型(假设是struct):
struct City { let name: String let gmtDiff: Int let myGMTDiff: Int // 可以在初始化时传入当前时区的偏移 // 动态计算当前时间 var currentTime: String { let timeInterval = Double(gmtDiff - myGMTDiff) * 3600.0 return Date().addingTimeInterval(timeInterval) .formatted(date: .omitted, time: .standard) } }
- 简化Timer逻辑,不需要修改数据源,只刷新可见行:
clockTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [self] t in let visibleRows = citiesTableView.indexPathsForVisibleRows ?? [] citiesTableView.reloadRows(at: visibleRows, with: .none) }
- 在cell的配置方法里直接使用计算属性:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "CityCell", for: indexPath) let city = cities[indexPath.row] cell.textLabel?.text = city.name cell.detailTextLabel?.text = city.currentTime return cell }
这个方案更高效,因为不需要频繁修改数据源,Timer的操作更轻量,也从根本上避免了删除时的数据同步问题。
内容的提问来源于stack exchange,提问作者psb
相关产品推荐
相关产品推荐

