第二部分:MVVM 在 SwiftUI 中的实现
第4章:设计 View 层
案例:基于 Todo 模型的列表界面
1. 需求分析
我们需要实现一个简单的 Todo 列表界面,包含以下功能:
- 显示 Todo 项的列表(标题、完成状态)
- 支持新增 Todo 项
- 支持标记 Todo 项为完成/未完成
- 支持删除 Todo 项
2. 视图结构设计
struct TodoListView: View {
@ObservedObject var viewModel: TodoListViewModel
var body: some View {
NavigationStack {
List {
ForEach(viewModel.todos) { todo in
TodoRowView(todo: todo)
.onTapGesture {
viewModel.toggleCompletion(for: todo.id)
}
}
.onDelete(perform: viewModel.deleteTodo)
}
.navigationTitle("Todo List")
.toolbar {
Button(action: viewModel.addNewTodo) {
Image(systemName: "plus")
}
}
}
}
}
struct TodoRowView: View {
let todo: Todo
var body: some View {
HStack {
Image(systemName: todo.isCompleted ? "checkmark.circle.fill" : "circle")
.foregroundColor(todo.isCompleted ? .green : .gray)
Text(todo.title)
Spacer()
}
}
}
3. 状态管理实现
class TodoListViewModel: ObservableObject {
@Published var todos: [Todo] = []
func addNewTodo() {
let newTodo = Todo(
id: UUID(),
title: "New Task \(todos.count + 1)",
isCompleted: false
)
todos.append(newTodo)
}
func toggleCompletion(for id: UUID) {
if let index = todos.firstIndex(where: { $0.id == id }) {
todos[index].isCompleted.toggle()
}
}
func deleteTodo(at offsets: IndexSet) {
todos.remove(atOffsets: offsets)
}
}
4. 数据模型定义
struct Todo: Identifiable {
let id: UUID
var title: String
var isCompleted: Bool
}
5. 预览实现
struct TodoListView_Previews: PreviewProvider {
static var previews: some View {
let viewModel = TodoListViewModel()
viewModel.todos = [
Todo(id: UUID(), title: "Buy groceries", isCompleted: false),
Todo(id: UUID(), title: "Finish report", isCompleted: true)
]
return TodoListView(viewModel: viewModel)
}
}
6. 关键实现说明
- 数据流:ViewModel 通过
@Published属性包装器发布数据变更,View 通过@ObservedObject监听变化 - 交互处理:用户操作通过 ViewModel 方法处理,保持 View 的简洁性
- 组件拆分:将列表项拆分为独立的
TodoRowView组件,提高可维护性 - 状态管理:使用 SwiftUI 原生的状态管理方案,避免引入复杂的状态管理库
7. 扩展思考
- 如何添加编辑功能?
- 如何实现分类筛选?
- 如何添加本地持久化存储?
