RxSwift+MVVM项目实战-多分组TableView+MJRefresh+RxAlamofire+HandyJSON的使用

简介: RxSwift+MVVM项目实战-多分组TableView+MJRefresh+RxAlamofire+HandyJSON的使用

本文主要结合前两篇对多分组UITableView的使用进行介绍,以下是前两篇内容,可直接点击跳转:

1. RxSwift+MVVM项目实战-多分组UITableView结合RxDataSources的使用

2. RxSwift+MVVM项目实战-多分组UITableView+RxDataSources+MJRefresh的使用

效果图


主要是利用RxAlamofire请求数据,以及HandyJSON对返回的json数据做处理;主要分析MuchGroupViewModelMuchGroupViewController,其他模块已在之前文章分析过;

RxAlamofire:基于Swift版本Alamofire的HTTP请求,利用RxSwift进一步封装而来;具体使用方式可参考 https://github.com/RxSwiftCommunity/RxAlamofire

HandyJson:基于Swift编写的字典转模型框架;具体使用方式可参考 https://github.com/alibaba/handyjson


目录结构


MuchGroupViewModel

Input

refresh:接收点击刷新按钮的事件;

headerRefresh:接收下拉刷新事件;

footerRefresh:上拉加载更多事件;

struct Input {
    let refresh: Observable<Void>
    let headerRefresh: Observable<Void>
    let footerRefresh: Observable<Void>
}


Output

itemstableView的数据源;

endRefreshing:通知外界tableView什么情况下结束刷新,以及结束刷新的状态类型;

struct Output {
    let items: BehaviorRelay<[MuchGroupSection]>
    let endRefreshing: PublishRelay<LTRefreshEndState>
}


RespEvent

保存网络请求返回的数据,以及结束刷新的状态类型;

private enum RespEvent {
        case resp(_ data: [MuchGroupSection], _ state: LTRefreshEndState)
        var data: [MuchGroupSection] {
            switch self {
            case .resp(let data, _):
                return data
            }
        }
        var state: LTRefreshEndState {
            switch self {
            case .resp(_, let _state):
                return _state
            }
        }
    }


request函数

利用RxAlamofire发起网络请求,并返回网络请求经过处理后的数据,以及结束刷新的状态类型;

private func request(pageNum: Int) -> Observable<RespEvent> {
    let url = URL(string: "https://")!
    Logger("\(pageNum == 0 ? "下拉刷新" : "上拉加载")发起请求...")
    return RxAlamofire.requestJSON(.get, url)
        .map { _ in MuchGroupModel.json }
        .map(model: MuchGroupModel.self)
        .map ({ (model) -> RespEvent in
            Logger("收到请求结果...")
            var section = [MuchGroupSection]()
            ...
            if pageNum > 1  {
                return RespEvent.resp(section, .noMoreData)
            }else {
                return RespEvent.resp(section, .endRefreshing)
            }
        })
}


自定义的map函数

Observable做扩展,目的是将传入进来的字典,利用Handyjson转换为模型信号;

extension Observable where Element == [String : Any] {
    func map<T: HandyJSON>(model: T.Type) -> Observable<T> {
        self.map { (element) -> T in
            guard let ret = T.deserialize(from: element) else { fatalError("数据格式不正确") /** 此处自行处理 */ }
            return ret
        }
    }
}


transform

items:保存数据源,保证上下拉请求时,对数据进行操作;

endRefreshing:保存刷新状态,用于绑定到外界tableView上;

header:将刷新事件以及下拉刷新事件合并,任意一个收到事件后,均发起网络请求,并处理数据,然后绑定到items上,之后items抛出给外界,用于提供tableView的数据源,另外也将状态绑定到endRefreshing上,用于结束tableView的刷新;

footer:收到上拉加载更多的时候,发起请求,并将之前的数据和新的数据进行拼接,然后绑定到items上,另外也将状态绑定到endRefreshing上,用于结束tableView的刷新;


func transform(input: Input) -> Output {
    let items = BehaviorRelay<[MuchGroupSection]>(value: [])
    let endRefreshing = PublishRelay<LTRefreshEndState>()
    //下拉刷新数据,请求第0页的数据并转换为Observable<RespEvent>类型
    let header = Observable.of(input.refresh, input.headerRefresh).merge().flatMap {
        [unowned self] (v1) -> Observable<RespEvent> in
        self.pageNum = 0
        return self.request(pageNum: self.pageNum)
    }.share(replay: 1, scope: .whileConnected)
    //抛出给外界,用于绑定到tableView数据源
    header.map { $0.data }.bind(to: items).disposed(by: disposeBag)
    //拿到返回数据通知外界停止刷新
    header.map { $0.state }.bind(to: endRefreshing).disposed(by: disposeBag)
    //上拉加载更多数据,请求第1.2.3...页的数据并转换为Observable<RespEvent>类型
    let footer = input.footerRefresh.flatMap {[unowned self] (_) -> Observable<RespEvent> in
        self.pageNum += 1
        return self.request(pageNum: self.pageNum)
    }.share(replay: 1, scope: .whileConnected)
    //把当前页数据和前几页数据做拼接,并抛出给外界,用于绑定到tableView数据源
    footer.map { (event) -> [MuchGroupSection] in
        guard case .resp(let data, _) = event else { return items.value }
        return items.value + data
    }.bind(to: items).disposed(by: disposeBag)
    //拿到返回数据通知外界停止刷新
    footer.map { $0.state }.bind(to: endRefreshing).disposed(by: disposeBag)
    return Output(items: items, endRefreshing: endRefreshing)
}


ViewController

结合RxDataSources,为tableView提供数据源以及配置cell,然后将Outputitems绑定到tableView的数据源上,将endRefreshing变量绑定到前文所分析的scrollView的扩展上;

private func bindToViewModel() {
    let output = viewModel.transform(input: MuchGroupViewModel.Input(refresh: Observable.just(()),
                                           headerRefresh: tableView.mj_header!.rx.glt_refreshing
                                            .asObservable(),
                                           footerRefresh: tableView.mj_footer!.rx.glt_refreshing
                                            .asObservable()))
    let dataSource = RxTableViewSectionedReloadDataSource<MuchGroupSection> {
        [unowned self] (dataSource, tableView, indexPath, item) -> UITableViewCell in
        switch item {
        case .banner(let viewModel):
            let cell: MuchGroupBannerCell = self.cellWithTableView(tableView)
            //绑定
            cell.bind(to: viewModel)
            //按钮点击事件
            cell.openSubjuect.subscribe {[weak self] (_) in
                self?.openEvent(viewModel.model, indexPath)
            }.disposed(by: cell.disposeBag)
            return cell
        case .live(let viewModel):
            let cell: MuchGroupLiveCell = self.cellWithTableView(tableView)
            //绑定
            cell.bind(to: viewModel)
            //按钮点击事件
            cell.openSubjuect.subscribe {[weak self] (_) in
                self?.openEvent(viewModel.model, indexPath)
            }.disposed(by: cell.disposeBag)
            return cell
        }
    }
    output.items.bind(to: tableView.rx.items(dataSource: dataSource)).disposed(by: disposeBag)
    output.endRefreshing.bind(to: tableView.rx.endRefreshing).disposed(by: disposeBag)
    tableView.rx.itemSelected.subscribe {[weak self] (value) in
        if let indexPath = value.element {
            self?.tableView.deselectRow(at: indexPath, animated: true)
        }
        self?.navigationController?.pushViewController(MuchGroupViewController(), animated: true)
    }.disposed(by: disposeBag)
    refreshItem.rx.tap.subscribe {[weak self] (_) in
        self?.tableView.mj_header?.beginRefreshing()
    }.disposed(by: disposeBag)
    tableView.rx.setDelegate(self).disposed(by: disposeBag)
}



相关文章
|
7月前
|
开发框架 前端开发 JavaScript
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(5) -- 树列表TreeView的使用
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(5) -- 树列表TreeView的使用
|
7月前
|
设计模式 前端开发 开发者
Angular携手Material Design:探索设计模式下的UI组件开发之道——从按钮到对话框的全面实战演示
【8月更文挑战第31天】在现代Web应用开发中,Angular框架结合Material Design设计原则与组件库,显著提升了用户界面的质量与开发效率。本文通过具体代码示例,详细介绍如何在Angular项目中引入并使用Material Design的UI组件,包括按钮、表单和对话框等,帮助开发者快速构建美观且功能强大的应用。通过这种方式,不仅能提高开发效率,还能确保界面设计的一致性和高质量,为用户提供卓越的体验。
48 0
RxSwift+MVVM项目实战-多分组UITableView+RxDataSources+MJRefresh的使用
RxSwift+MVVM项目实战-多分组UITableView+RxDataSources+MJRefresh的使用
168 0
|
存储 Swift
RxSwift+MVVM项目实战-多分组UITableView结合RxDataSources的使用
RxSwift+MVVM项目实战-多分组UITableView结合RxDataSources的使用
401 0
RxSwift+MVVM项目实战-单分组UITableView的使用
RxSwift+MVVM项目实战-单分组UITableView的使用
273 0
|
存储
RxSwift+MVVM项目实战-单分组UITableView添加、删除、移动功能
RxSwift+MVVM项目实战-单分组UITableView添加、删除、移动功能
198 0
|
iOS开发 开发者
iOS开发-简述UITableView中cell的重用问题
iOS开发-简述UITableView中cell的重用问题
206 0
|
前端开发
[译] 实用的 MVVM 和 RxSwift
今天我们将使用 RxSwift 实现 MVVM 设计模式。对于那些刚接触 RxSwift 的人,我 在这里 专门做了一个部分来介绍。
1390 0
|
前端开发 C#
从PRISM开始学WPF(七)MVVM(三)事件聚合器EventAggregator?
原文:从PRISM开始学WPF(七)MVVM(三)事件聚合器EventAggregator? 从PRISM开始学WPF(一)WPF? 从PRISM开始学WPF(二)Prism? 从PRISM开始学WPF(三)Prism-Region? 从PRISM开始学WPF(四)Prism-Module? ...
1541 0