菜鸟AI - 让提示词生成更简单! 全站导航 全站导航
AI工具安装 新手教程 进阶教程 辅助资源 AI提示词 热点资讯 技术资讯 产业资讯 内容生成 模型技术 AI信息库

已有账号?

首页 > AI教程 > Swift MVVM架构实战:列表数据展示与交互完整指南
进阶教程

Swift MVVM架构实战:列表数据展示与交互完整指南

2026-05-31
阅读 0
热度 0
作者 菜鸟AI编辑部
摘要

摘要

基于MVVM架构,使用原生Swift实现列表数据展示与交互功能,涵盖数据渲染、单元格复用、点

Swift 基于MVVM架构实现完整列表数据展示与交互功能实战案例

一、架构简介

MVVM 在 iOS 开发圈子里已经算是主流标配了。跟传统的 MVC 相比,它把职责拆得更清楚:View 只管页面长什么样、用户怎么点;ViewModel 负责业务逻辑、数据处理和视图状态的管理;Model 就老老实实承载数据模型。这么一拆,视图和业务逻辑就彻底解耦了,代码可读性、可维护性,甚至单元测试的友好度,都上了一个台阶。对于列表类这种常规业务来说,MVVM 几乎是天然适配的。

Swift 基于MVVM架构实现完整列表数据展示与交互功能实战案例

这篇就以一个基础列表页为例,从零开始,把数据渲染、单元格复用、点击交互、数据刷新这些核心功能一一走通。全程用原生 Swift,适配 iOS 14+,不依赖第三方框架。

二、项目结构

目录划分清晰,严格遵循 MVVM 的分层逻辑:

├── Model // 数据模型层 ├── ViewModel // 业务逻辑层 ├── View // 视图层(控制器、自定义Cell)

三、代码实现

3.1 数据模型(Model)

先建一个列表数据源模型。采用 Codable 协议,方便后续网络数据解析。这里定义列表展示所需的几个核心字段:

import Foundation // 列表数据模型 struct ListModel: Codable { let id: Int let title: String let desc: String }

3.2 视图模型(ViewModel)

ViewModel 是整个架构的枢纽层。它封装了数据请求、数据数组管理、单元格数据赋值、点击事件回调——但绝不持有任何视图对象。通过闭包来回调数据刷新和点击事件,视图和逻辑之间就彻底松绑了。

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)

ListCell 封装了 UITableViewCell,只负责两件事:UI 布局和数据赋值。不处理任何业务逻辑,ViewModel 把数据传过来,它照单显示就行。

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 去处理,控制器只管“接活儿”。

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 展示。代码分层清晰,后期维护和扩展都省心。

如果后续项目需要扩展,可以在这个基础上继续加:下拉刷新、上拉加载更多、网络异常处理、空页面占位图等通用功能,适配更复杂的业务场景完全没问题。

来源:互联网

免责声明

本网站新闻资讯均来自公开渠道,力求准确但不保证绝对无误,内容观点仅代表作者本人,与本站无关。若涉及侵权,请联系我们处理。本站保留对声明的修改权,最终解释权归本站所有。

同类文章推荐

相关文章推荐

更多