Swift 基于MVVM架构实现完整列表数据展示与交互功能实战案例
一、架构简介
MVVM 是 iOS 开发中主流的架构模式,在传统 MVC 基础上做了职责拆分:View 负责页面视图展示与用户交互、ViewModel 承担业务逻辑、数据处理与视图状态管理、Model 仅承载数据模型。该架构实现视图与业务逻辑解耦,代码可读性、可维护性和单元测试性大幅提升,非常适合列表类常规业务开发。
本文以基础列表页为例,从零实现数据渲染、单元格复用、点击交互、数据刷新等核心功能,全程使用原生 Swift 语法,适配 iOS 14+ 系统。
二、项目结构
整体目录划分清晰,严格遵循 MVVM 分层:
arduino
├── Model // 数据模型层
├── ViewModel // 业务逻辑层
├── View // 视图层(控制器、自定义Cell)
三、代码实现
3.1 数据模型(Model)
创建列表数据源模型,采用 Codable 协议,方便后续网络数据解析,定义列表展示所需字段。
swift
import Foundation
// 列表数据模型
struct ListModel: Codable {
let id: Int
let title: String
let desc: String
}
3.2 视图模型(ViewModel)
ViewModel 作为中间层,封装数据请求、数据数组管理、单元格数据赋值、点击事件回调,不持有任何视图对象。使用闭包回调数据刷新与点击事件,解耦视图与逻辑。
swift
import Foundation
class ListViewModel {
// 数据源数组
private(set) var dataArray: [ListModel] = []
// 数据刷新回调
var reloadDataClosure: (() -> Void)?
// 单元格点击回调
var cellClickClosure: ((ListModel) -> Void)?
// 模拟请求本地/网络数据
func requestListData() {
// 模拟异步网络请求
DispatchQueue.global().asyncAfter(deadline: .now() + 0.5) { [weak self] in
guard let self = self else { return }
// 构造测试数据
let tempData = [
ListModel(id: 1, title: "Swift基础语法", desc: "Swift数据类型、函数与流程控制讲解"),
ListModel(id: 2, title: "UIKit控件使用", desc: "UILabel、UIButton、UITableView实战"),
ListModel(id: 3, title: "MVVM架构思想", desc: "架构分层与解耦设计原则")
]
self.dataArray = tempData
// 主线程回调刷新视图
DispatchQueue.main.async {
self.reloadDataClosure?()
}
}
}
// 获取单个单元格数据
func getCellModel(index: Int) -> ListModel? {
guard index >= 0, index < dataArray.count else { return nil }
return dataArray[index]
}
// 触发单元格点击事件
func didSelectRow(index: Int) {
guard let model = getCellModel(index: index) else { return }
cellClickClosure?(model)
}
}
3.3 自定义单元格(View)
封装 UITableViewCell,仅负责 UI 布局与数据赋值,不处理业务逻辑,接收 ViewModel 传递的数据进行展示。
swift
import UIKit
class ListCell: UITableViewCell {
// UI控件
private let titleLabel: UILabel = {
let label = UILabel()
label.font = UIFont.systemFont(ofSize: 16, weight: .semibold)
return label
}()
private let descLabel: UILabel = {
let label = UILabel()
label.font = UIFont.systemFont(ofSize: 13)
label.textColor = .gray
label.numberOfLines = 0
return label
}()
// 初始化布局
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// 页面布局
private func setupUI() {
contentView.addSubview(titleLabel)
contentView.addSubview(descLabel)
titleLabel.translatesAutoresizingMaskIntoConstraints = false
descLabel.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
titleLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 12),
titleLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),
titleLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16),
descLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 8),
descLabel.leadingAnchor.constraint(equalTo: titleLabel.leadingAnchor),
descLabel.trailingAnchor.constraint(equalTo: titleLabel.trailingAnchor),
descLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -12)
])
}
// 绑定数据
func configData(model: ListModel) {
titleLabel.text = model.title
descLabel.text = model.desc
}
}
3.4 主控制器(View)
控制器作为视图载体,仅负责创建视图、绑定 ViewModel 回调、响应页面交互,所有业务逻辑交由 ViewModel 处理。
swift
import UIKit
class ListViewController: UIViewController {
// 初始化ViewModel
private let viewModel = ListViewModel()
private let tableView = UITableView()
private let cellID = "ListCell"
override func viewDidLoad() {
super.viewDidLoad()
setupBase()
setupTableView()
bindViewModel()
// 发起数据请求
viewModel.requestListData()
}
private func setupBase() {
view.backgroundColor = .white
title = "MVVM列表实战"
}
// 初始化表格
private func setupTableView() {
view.addSubview(tableView)
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.frame = view.bounds
tableView.delegate = self
tableView.dataSource = self
tableView.register(ListCell.self, forCellReuseIdentifier: cellID)
tableView.tableFooterView = UIView()
}
// 绑定ViewModel回调
private func bindViewModel() {
// 数据刷新
viewModel.reloadDataClosure = { [weak self] in
self?.tableView.reloadData()
}
// 单元格点击
viewModel.cellClickClosure = { model in
print("点击条目:\(model.title),ID:\(model.id)")
let alert = UIAlertController(title: "点击提示", message: model.title, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "确定", style: .default))
self.present(alert, animated: true)
}
}
}
// UITableView 代理与数据源
extension ListViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return viewModel.dataArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellID, for: indexPath) as? ListCell,
let model = viewModel.getCellModel(index: indexPath.row) else {
return UITableViewCell()
}
cell.configData(model: model)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
viewModel.didSelectRow(index: indexPath.row)
}
}
四、功能测试与总结
- 运行效果:启动项目后,页面会异步加载模拟数据,自动渲染列表;点击任意单元格,弹出提示弹窗并打印日志,单元格正常取消选中状态。
- 架构优势:Model 只负责数据存储,无任何视图相关代码;ViewModel 独立处理数据请求、业务逻辑,可单独做单元测试;View 仅专注 UI 展示,代码分层清晰。
- 扩展方向:可在此基础上拓展下拉刷新、上拉加载更多、网络异常处理、空页面占位图等通用功能,适配复杂业务场景。
海量精选技术文档和实战案例持续更新,敬请关注【风骏时光少年】公众号