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

iOS TableView切换待办状态后数据不显示问题求助

问题描述

应用启动时可正常显示ToDo数组,但将待办项切换至“In Progress”或“Done”状态后,再次返回“To Do”状态时,toDoGoals数组无法在TableView中显示,仅能看到最后点击的分区内容。

问题复现截图

  • 应用启动并选中ToDo状态
    应用启动并选中ToDo状态
  • 选中InProgress状态
    选中InProgress状态
  • 选中Done状态
    选中Done状态
  • 再次选中ToDo状态
    再次选中ToDo状态

现有代码

HomeViewModel

import Foundation

protocol HomeViewModelInterface {
    var view: HomeScreenInterface? { get set }
    func viewDidLoad()
    func viewToDoGoals()
    func viewInProgressGoals()
    func viewDoneGoals()
}

final class HomeViewModel {
    weak var view: HomeScreenInterface?
    var progressSections: [String] = ["To Do","In Progress","Done"]
    
    lazy var toDoGoals: [ToDoModel] = [
        ToDoModel(title: "ToDo 1"),
        ToDoModel(title: "Todo 2"),
        ToDoModel(title: "ToDo 3")
    ]
    
    lazy var inProgressGoals: [ToDoModel] = [
        ToDoModel(title: "Progress 1"),
        ToDoModel(title: "Progress 2"),
        ToDoModel(title: "Progress 3")
    ]
    
    lazy var done: [ToDoModel] = [
        ToDoModel(title: "Done 1"),
        ToDoModel(title: "Done 2"),
        ToDoModel(title: "Done 3")
    ]
}

extension HomeViewModel: HomeViewModelInterface {
    
    func viewDidLoad() {
        view?.configureVC()
        view?.configureCollectionView()
        view?.configureTableView()
    }
    
    func viewToDoGoals() {
        self.view?.updateTableView(with: self.toDoGoals)
        print("View todo")
    }
    
    func viewInProgressGoals() {
        self.view?.updateTableView(with: self.inProgressGoals)
        print("View progress")
    }
    
    func viewDoneGoals() {
        self.view?.updateTableView(with: self.done)
        print("View Done.")
    }
}

HomeScreen

protocol HomeScreenInterface: AnyObject {
    func configureVC()
    func configureCollectionView()
    func configureTableView()
    func updateTableView(with goals: [ToDoModel])
}

final class HomeScreen: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        viewModel.view = self
        viewModel.viewDidLoad()
    }
}

extension HomeScreen: HomeScreenInterface {

    func configureVC() {
        title = "To Do ✅"
        view.backgroundColor = .systemBackground
    }
    // Update TableView Array
    func updateTableView(with goals: [ToDoModel]) {
        viewModel.toDoGoals = goals
        DispatchQueue.main.async {
            self.goalsTableView.reloadData()
        }
    }
}

TableView 代理与数据源方法

extension HomeScreen: UITableViewDelegate, UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return viewModel.toDoGoals.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: GoalsCells.reuseIdentifier, for: indexPath) as? GoalsCells else {
            return UITableViewCell()
        }
        cell.delegate = self
        
        let goals = viewModel.toDoGoals[indexPath.row]
        cell.setGoalCell(goal: goals.title, checked: goals.isComplete)
        return cell
    }
}

CollectionView 代理与数据源方法

extension HomeScreen: UICollectionViewDelegate, UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ProgressCells.reuseIdentifier, for: indexPath) as? ProgressCells else {
            return UICollectionViewCell()
        }
        
        cell.setCell(progress: viewModel.progressSections[indexPath.item])
        return cell
    }
    
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        switch indexPath.item {
        case 0: // To Do
            self.viewModel.viewToDoGoals()
        case 1: // In Progress
            self.viewModel.viewInProgressGoals()
        case 2: // Done
            self.viewModel.viewDoneGoals()
        default:
            break
        }
    }
}

ToDoModel

import Foundation

struct ToDoModel {
    let title: String
    let isComplete: Bool
    
    init(title: String, isComplete: Bool = false) {
        self.title = title
        self.isComplete = isComplete
    }
    
    func completeToggled() -> ToDoModel {
        return ToDoModel(title: title, isComplete: !isComplete)
    }
}

问题根源

核心问题出在HomeScreen的updateTableView(with:)方法中:每次切换状态时,你把传入的目标数组直接赋值给了viewModel.toDoGoals,这会覆盖掉toDoGoals的原始数据。比如切换到“In Progress”后,toDoGoals就变成了inProgressGoals的内容,当你再次切回“To Do”时,viewModel.viewToDoGoals()传递的已经是被覆盖后的数组,而非初始的ToDo数据。

修复方案

通过在ViewModel中维护一个当前显示数组,避免修改原始数据数组,就能解决这个问题:

1. 修改HomeViewModel

添加currentDisplayedGoals属性存储当前要展示的数据,同时把原始数据数组设为私有避免误修改:

final class HomeViewModel {
    weak var view: HomeScreenInterface?
    var progressSections: [String] = ["To Do","In Progress","Done"]
    
    // 私有原始数据,避免被外部修改
    private lazy var toDoGoals: [ToDoModel] = [
        ToDoModel(title: "ToDo 1"),
        ToDoModel(title: "Todo 2"),
        ToDoModel(title: "ToDo 3")
    ]
    
    private lazy var inProgressGoals: [ToDoModel] = [
        ToDoModel(title: "Progress 1"),
        ToDoModel(title: "Progress 2"),
        ToDoModel(title: "Progress 3")
    ]
    
    private lazy var doneGoals: [ToDoModel] = [
        ToDoModel(title: "Done 1"),
        ToDoModel(title: "Done 2"),
        ToDoModel(title: "Done 3")
    ]
    
    // 当前要显示的数组
    var currentDisplayedGoals: [ToDoModel] = []
}

extension HomeViewModel: HomeViewModelInterface {
    func viewDidLoad() {
        // 初始化默认显示ToDo数据
        currentDisplayedGoals = toDoGoals
        view?.configureVC()
        view?.configureCollectionView()
        view?.configureTableView()
        view?.updateTableView() // 通知View更新初始数据
    }
    
    func viewToDoGoals() {
        currentDisplayedGoals = toDoGoals
        view?.updateTableView()
        print("View todo")
    }
    
    func viewInProgressGoals() {
        currentDisplayedGoals = inProgressGoals
        view?.updateTableView()
        print("View progress")
    }
    
    func viewDoneGoals() {
        currentDisplayedGoals = doneGoals
        view?.updateTableView()
        print("View Done.")
    }
}

2. 修改HomeScreenInterface

移除updateTableView的参数,因为不需要再传递数组:

protocol HomeScreenInterface: AnyObject {
    func configureVC()
    func configureCollectionView()
    func configureTableView()
    func updateTableView()
}

3. 修改HomeScreen的updateTableView实现

不再修改viewModel.toDoGoals,直接刷新TableView:

extension HomeScreen: HomeScreenInterface {
    // ... 其他方法保持不变
    
    func updateTableView() {
        DispatchQueue.main.async {
            self.goalsTableView.reloadData()
        }
    }
}

4. 修改TableView数据源方法

改为使用currentDisplayedGoals作为数据源:

extension HomeScreen: UITableViewDelegate, UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return viewModel.currentDisplayedGoals.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: GoalsCells.reuseIdentifier, for: indexPath) as? GoalsCells else {
            return UITableViewCell()
        }
        cell.delegate = self
        
        let goals = viewModel.currentDisplayedGoals[indexPath.row]
        cell.setGoalCell(goal: goals.title, checked: goals.isComplete)
        return cell
    }
}

这样修改后,原始的三个数据数组始终保持初始状态,每次切换状态只是更新currentDisplayedGoals的值,切回“To Do”时会重新赋值原始的ToDo数据,TableView就能正常显示内容了。


内容的提问来源于stack exchange,提问作者Kerem DEMİR

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 22:12:03