使用RXSwift撸一个MVVM架构的Tableview -- part 2

457 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第4天,点击查看活动详情


前言

又来撸RXSwift了

如果您还没有阅读上一部分,它可以在这里找到。在上一篇中,我们实现了如何设置只有一种cell的 TableView。

在本文中,我们将实现如何设置具有一个section,多种cell的tableview

那么就需要引入一个新的库

pod RxDataSources

这个库可以帮我们实现更多功能,使用它可以大大减少我们的工作量。更多功能可以自行百度。

正文

项目的目录结构还是一样。

截屏2022-05-27 下午2.46.42.png

第一步 定义Model

咱们在Model文件夹中创建两个文件 Student.swiftTeacher.swift 分别对应两个模型

import Foundation

struct Student {
    let name : String
    let lastName : String
}
import Foundation

struct Teacher {
    let name : String
    let lastName : String
    let major : String
}

第二步 视图模型(viewModel)

在ViewModel文件夹下新建StudentViewModel 然后导入

import RxDataSources

然后定义一个section 使用enum

enum TableViewSection {
    case main
}

定义一个item

enum TableViewItem {
    case student(info: Student)
    case teacher(info: Teacher)
}

然后定义一个SectionOfStudent

typealias SectionOfStudent = SectionModel<TableViewSection,TableViewItem>

这里的SectionModel是来自RxDataSources,它有两个参数,一个是Section,另一个是Item,咱们就用上面定义好的TableViewSectionTableViewItem传入

所以,SectionOfStudent 就相当于一个section里的所有内容了,初始化它也十分简单

SectionOfStudent(model: .main, items: subItems)

这里的.main 也可以只是个枚举,可以自定义为别的,也可以传空。

subItems就一个数组,里面就是需要传入的数据模型。

接下来 我们在进行模拟数据请求

class StudentViewModel {
    let items = PublishSubject<[SectionOfStudent]>()
    
    // 模拟请求
    func getInformation() {
        // 定义一个数组模型
        var subItems = [TableViewItem]()
        
        subItems = ([
            .student(info: Student(name: "Liam", lastName: "swift")),
            .teacher(info: Teacher(name: "Emma", lastName: "Das", major: "Math")),
            .student(info: Student(name: "Ava", lastName: "Banks")),
            .teacher(info: Teacher(name: "Bill", lastName: "Cooper", major: "Math")),
            .student(info: Student(name: "Caden", lastName: "Smith")),
            .teacher(info: Teacher(name: "Isabella", lastName: "Watson", major: "Math")),
        ])
        
        // 发送数据
        items.onNext([SectionOfStudent(model: .main, items: subItems)])
    }
}

这样,咱们就完成了一个,只有一个section,和多个cell的数据

第三步 视图(View)

咱们在View文件夹下创建一个StudentViewController

同样的设置 bagviewModel

private let bag = DisposeBag()
private let viewModel = StudentViewModel()

然后设置两个懒加载变量 ConfigureCelldataSource

private lazy var dataSource = RxTableViewSectionedReloadDataSource<SectionOfStudent>(configureCell: configureCell)
    
    
private lazy var configureCell: RxTableViewSectionedReloadDataSource<SectionOfStudent>.ConfigureCell = { [unowned self] (dataSource, tableView, indexPath, item) in
        
        switch item {
        case .student(let info):
            return self.configStudentCell(student: info, atIndex: indexPath)
        case .teacher(let info):
            return self.configTeacherCell(teacher: info, atIndex: indexPath)
        }
        
    }

configureCell 是一个闭包cell 通过传入的item来返回不同的cell

dataSource 接收一个 ConfigureCell 类型作为参数,所以我们将 ConfigureCell 实例传递给 dataSource 并 将数据直接绑定到 TableView。

接下来我们在viewDidLoad中可以做一些绑定操作了

 override func viewDidLoad() {
        super.viewDidLoad()
        tableView.rx.setDelegate(self).disposed(by: bag)
        tableView.register(UINib(nibName: "StudentCell", bundle: nil), forCellReuseIdentifier: "StudentTableViewCell")
        tableView.register(UINib(nibName: "TeacherCell", bundle: nil), forCellReuseIdentifier: "TeacherTableViewCell")
        
        viewModel.items
        .bind(to: tableView.rx.items(dataSource: dataSource))
        .disposed(by: bag)
        
        viewModel.getInformation()
    }

这里的 StudentCellTeacherCell都是自定义的,可以自行处理。

viewModel.getInformation() 表示开始请求数据(当然这里是假数据)

最后再写两个创建cell的函数

func configStudentCell(student: Student, atIndex: IndexPath) -> UITableViewCell {
        guard let cell = self.tableView.dequeueReusableCell(withIdentifier: "StudentCell", for: atIndex) as? StudentCell else {
            return UITableViewCell()
        }
        cell.viewModel = student
        return cell
    }
    
func configTeacherCell(teacher: Teacher, atIndex: IndexPath) -> UITableViewCell {
        guard let cell = self.tableView.dequeueReusableCell(withIdentifier: "TeacherCell", for: atIndex) as? TeacherCell else {
            return UITableViewCell()
        }
        cell.viewModel = teacher
        return cell
    }

UITableViewDelegate

extension StudentViewController: UITableViewDelegate {
    
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 150
    }
}

就基本上完成了

可以看看效果图

截屏2022-05-27 下午4.00.44.png

结语

总体来说 使用RxDataSources来进行tableview的处理,难度又上升了,里面又涉及到许多新的函数和变量需要理解,不过理解后处理起来就方便许多,代码量也少了。