SwiftUI中切换Todo项时如何实时更新完成计数
SwiftUI TodoList 已完成计数实时更新问题解决
问题根源
你遇到的问题核心在于:TodoItem是普通类,修改它的isComplete属性时,TodosRepository中@Published var todos不会发送更新通知——@Published仅监听数组本身的引用变更(比如添加、删除元素),数组内部元素的属性修改无法触发它的更新,因此MainView的已完成计数不会实时刷新。
解决方案
以下是两种可行的修复方案,你可以根据场景选择:
方案一:将TodoItem改为ObservableObject
让TodoItem遵循ObservableObject协议,并用@Published标记isComplete属性,这样属性变化时会自动通知视图更新。
修改TodoItem类
class TodoItem : Identifiable, ObservableObject { let id: UUID let itemDescription: String let itemCreatedAt: Date @Published var isComplete: Bool init(text: String){ self.id = UUID() self.itemDescription = text self.itemCreatedAt = Date.now self.isComplete = false } }
修改TodoListItemView
使用@ObservedObject接收todoItem,确保Toggle的变化能触发视图更新:
struct TodoListItemView: View { @ObservedObject var todoItem: TodoItem var body: some View { HStack { VStack(alignment: .leading) { Text(todoItem.itemDescription) .font(.headline) Text(todoItem.itemCreatedAt.debugDescription) .fontWeight(.thin) .font(.subheadline) } Spacer() Toggle(isOn: $todoItem.isComplete) {} .frame(width: 50) } } }
调整MainView的ForEach
由于TodoItem现在是ObservableObject,需要用$绑定来确保仓库感知到变化:
ForEach($todosRepository.todos){ $todo in TodoListItemView(todoItem: todo) .foregroundColor(.white) .padding(.bottom, 10) Divider() .background(Color(.lightGray)) }
方案二:通过仓库统一处理状态修改(推荐MVVM模式)
将Todo的状态修改逻辑集中到TodosRepository,确保数据变更时触发视图更新,更符合MVVM的架构设计。
给TodosRepository添加状态修改方法
class TodosRepository : ObservableObject{ @Published var todos: [TodoItem] = [] var completedTodos: [TodoItem] { todos.filter({ $0.isComplete == true }) } init() {} func addTodo(text: String) { let todo = TodoItem(text: text) todos.append(todo) } // 新增:切换Todo完成状态的方法 func toggleTodoCompletion(_ todo: TodoItem) { guard let index = todos.firstIndex(where: { $0.id == todo.id }) else { return } // 替换数组元素触发@Published更新 var updatedTodo = todo updatedTodo.isComplete.toggle() todos[index] = updatedTodo // 或者手动发送更新通知: // objectWillChange.send() } }
修改TodoListItemView,使用回调传递事件
struct TodoListItemView: View { let todoItem: TodoItem let onToggle: () -> Void // 回调闭包,用于通知父视图切换状态 var body: some View { HStack { VStack(alignment: .leading) { Text(todoItem.itemDescription) .font(.headline) Text(todoItem.itemCreatedAt.debugDescription) .fontWeight(.thin) .font(.subheadline) } Spacer() // 使用constant绑定,通过回调处理状态变更 Toggle(isOn: .constant(todoItem.isComplete), action: onToggle) .frame(width: 50) } } }
在MainView中传递回调
ForEach(todosRepository.todos){ todo in TodoListItemView(todoItem: todo, onToggle: { todosRepository.toggleTodoCompletion(todo) }) .foregroundColor(.white) .padding(.bottom, 10) Divider() .background(Color(.lightGray)) }
方案对比
- 方案一:实现简单,适合小型项目或快速原型开发,直接让模型属性变更通知视图。
- 方案二:将数据修改逻辑集中管理,降低视图与模型的耦合,利于后续功能扩展和维护,更符合iOS开发的最佳实践。
内容的提问来源于stack exchange,提问作者anb2022
相关产品推荐
相关产品推荐

