概念
讲述MVVM的文章很多,我就不多描述了,本篇主要讲的是在swift项目中的应用
模块
MVVM模块中,view、controller、model、viewModel,其中view和controller可以看成同类的,也可以不同类的
交互关系
view -> controller、viewModel
model -> viewModel
viewModel -> model、controller、view
controller -> view、viewModel
其中,
-
model
仅仅作为数据模型的形式存在(从网络端等获取的数据模型),不进行数据的加工(如时间戳转为字符串时间、一些字符串的加工等) -
viewModel
1、获取数据model(网络请求等方式获取)
2、对model进行数据加工,并转为viewModel属性,用于数据展示 -
view
1、和viewModel交互:将viewModel中的数据展示
2、和controller交互:用于UI渲染
不和model交互 -
controller
1、和view交互
2、和viewModel交互(获取数据等)
3、不与model相关联
案例
此案例为一个从网络获取新闻数据列表,渲染的过程
第一步,在viewModel(WCNewsLoaderViewModel)中获取网络数据,并将网络数据转为用于展示的viewModel(WCNewsListViewModel),需要展示的内容包含图片、标题、内容、时间数据
WCNewsModel
数据模型-仅仅作为一个模型来接收数据(网络请求等方式获取的数据)
class WCNewsModel: NSObject {
var iconName:String?
var title:String?
var content:String?
var time:String?
}
WCNewsLoaderViewModel
网络请求的viewModel-进行网络请求的过程,并且将请求到的数据转为可以直接使用的另外一个viewModel
class WCNewsLoaderViewModel: NSObject {
var listViewModels:[WCNewsListViewModel] = []
//获取数据
public func getData(_ closure:@escaping()->()) {
//模拟网络请求延时获取数据
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
let array = ["pic_area_balcony", "pic_area_bedroom", "pic_area_corridor", "pic_area_kitchen", "pic_area_livingroom", "pic_area_study", "pic_area_toilet", "pic_area_toilet", "pic_area_toilet"]
for (index, item) in array.enumerated() {
let model = WCNewsModel()
model.iconName = item
model.title = "这里是标题\(index)"
if index == 1 {
model.title = "今天晴"
}
model.content = "内容"
if index == 2 {
model.content = "这里是内容这里是内容这里是内容这里是内容这里是内容这里是内容这里是内容这里是内容这里是内容这里是内容"
}
model.time = "2022-09-15 15:57:\(index)"
let listViewModel = WCNewsListViewModel(model: model)
self.listViewModels.append(listViewModel)
}
closure()
}
}
}
WCNewsListViewModel
数据的viewModel-已经处理好数据的viewModel,可直接用于数据的展示
class WCNewsListViewModel: NSObject {
var iconImage:UIImage?
var title:String?
var content:String?
var time:String?
var model:WCNewsModel
init(model:WCNewsModel) {
self.model = model
super.init()
self.handleModel()
}
private func handleModel() {
if let iconName = self.model.iconName {
self.iconImage = UIImage(named: iconName)
} else {
self.iconImage = nil
}
self.title = "标题:" + (self.model.title ?? "")
self.content = "正文:" + (self.model.content ?? "")
self.time = "时间:" + (self.model.time ?? "")
}
}
WCNewsViewController
控制器-用于绘制UI、协调交互等
class WCNewsViewController: UIViewController {
private lazy var newsLoaderViewModel:WCNewsLoaderViewModel = WCNewsLoaderViewModel()
private lazy var tableView:UITableView = {
let tableView = UITableView.init(frame: self.view.bounds)
tableView.backgroundColor = .white
tableView.separatorStyle = .none
tableView.delegate = self
tableView.dataSource = self
return tableView
}()
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .gray
self.view.addSubview(self.tableView)
self.getData()
}
deinit {
print("deinit ===" + NSStringFromClass(self.classForCoder))
}
}
extension WCNewsViewController {
private func getData() {
self.newsLoaderViewModel.getData {
self.tableView.reloadData()
}
}
}
extension WCNewsViewController:UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.newsLoaderViewModel.listViewModels.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = WCNewsTableViewCell.wc_baseTableViewCell(tableView: tableView) as! WCNewsTableViewCell
let listViewModel = self.newsLoaderViewModel.listViewModels[indexPath.row]
cell.listViewModel = listViewModel
return cell
}
}
总结
1、controller用于绘制UI、协调各种行为等
2、view用于绘制UI
3、model用于数据模型存储
4、viewModel用于网络请求、数据处理等