第二部分:MVUI 在 SwiftUI 中的实现
第6章:MVVM 的数据流
案例:Todo 应用的完整数据流
1. 案例概述
在本案例中,我们将实现一个完整的 Todo 应用数据流,涵盖以下功能:
- 显示待办事项列表
- 添加新待办事项
- 标记事项完成状态
- 删除事项
通过此案例,您将理解 MVVM 中数据如何从 Model 层流向 View 层,以及用户交互如何通过 ViewModel 更新 Model。
2. 数据流实现步骤
2.1 Model 层设计
struct TodoItem: Identifiable, Codable {
let id: UUID
var title: String
var isCompleted: Bool
var createdAt: Date
}
2.2 ViewModel 实现
class TodoViewModel: ObservableObject {
@Published var todos: [TodoItem] = []
func addTodo(title: String) {
let newTodo = TodoItem(
id: UUID(),
title: title,
isCompleted: false,
createdAt: Date()
)
todos.append(newTodo)
}
func toggleCompletion(for todo: TodoItem) {
if let index = todos.firstIndex(where: { $0.id == todo.id }) {
todos[index].isCompleted.toggle()
}
}
func deleteTodo(at indexSet: IndexSet) {
todos.remove(atOffsets: indexSet)
}
}
2.3 View 层实现
struct TodoListView: View {
@StateObject var viewModel = TodoViewModel()
@State private var newTodoTitle = ""
var body: some View {
NavigationView {
VStack {
// 输入新待办事项
HStack {
TextField("新增待办事项", text: $newTodoTitle)
.textFieldStyle(RoundedBorderTextFieldStyle())
Button(action: {
guard !newTodoTitle.isEmpty else { return }
viewModel.addTodo(title: newTodoTitle)
newTodoTitle = ""
}) {
Image(systemName: "plus.circle.fill")
}
}
.padding()
// 待办事项列表
List {
ForEach(viewModel.todos) { todo in
HStack {
Image(systemName: todo.isCompleted ? "checkmark.circle.fill" : "circle")
.foregroundColor(todo.isCompleted ? .green : .gray)
.onTapGesture {
viewModel.toggleCompletion(for: todo)
}
Text(todo.title)
.strikethrough(todo.isCompleted)
Spacer()
Text(todo.createdAt, style: .time)
.font(.caption)
.foregroundColor(.gray)
}
}
.onDelete(perform: viewModel.deleteTodo)
}
}
.navigationTitle("待办事项")
}
}
}
3. 数据流分析
初始化流程:
- View 创建时实例化
TodoViewModel(@StateObject) - ViewModel 初始化
@Published的todos数组
- View 创建时实例化
用户添加事项:
- 用户在 TextField 输入文本 → 更新
newTodoTitle(@State) - 点击添加按钮 → 调用 ViewModel 的
addTodo方法 - ViewModel 更新
todos数组 → 触发 View 自动更新
- 用户在 TextField 输入文本 → 更新
状态切换流程:
- 用户点击完成状态图标 → 调用 ViewModel 的
toggleCompletion - ViewModel 修改对应 TodoItem 的
isCompleted属性 @Published属性变更触发 View 重绘
- 用户点击完成状态图标 → 调用 ViewModel 的
删除流程:
- 用户滑动删除 → 调用 ViewModel 的
deleteTodo - ViewModel 更新数据 → View 同步更新 UI
- 用户滑动删除 → 调用 ViewModel 的
4. 关键点说明
- 单向数据流:所有数据修改都通过 ViewModel 进行,View 不直接修改 Model
- 自动绑定:
@Published属性与 View 的自动绑定关系 - 状态管理:使用
@StateObject确保 ViewModel 生命周期与 View 一致
5. 完整项目结构建议
TodoApp/
├── Models/
│ └── TodoItem.swift
├── ViewModels/
│ └── TodoViewModel.swift
├── Views/
│ ├── TodoListView.swift
│ └── TodoItemView.swift
└── Utilities/
└── TodoDataStore.swift
6. 扩展思考
- 如何添加数据持久化功能?
- 如何实现分类过滤功能?
- 如何添加事项优先级功能?
通过这个案例,您已经掌握了 MVVM 在 SwiftUI 中的完整数据流实现方式。接下来可以尝试扩展更多功能来巩固理解。
