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)
}



相关文章
|
4月前
|
开发框架 前端开发 JavaScript
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(5) -- 树列表TreeView的使用
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(5) -- 树列表TreeView的使用
RxSwift+MVVM项目实战-多分组UITableView+RxDataSources+MJRefresh的使用
RxSwift+MVVM项目实战-多分组UITableView+RxDataSources+MJRefresh的使用
154 0
RxSwift+MVVM项目实战-单分组UITableView的使用
RxSwift+MVVM项目实战-单分组UITableView的使用
260 0
|
存储 Swift
RxSwift+MVVM项目实战-多分组UITableView结合RxDataSources的使用
RxSwift+MVVM项目实战-多分组UITableView结合RxDataSources的使用
351 0
|
存储
RxSwift+MVVM项目实战-单分组UITableView添加、删除、移动功能
RxSwift+MVVM项目实战-单分组UITableView添加、删除、移动功能
160 0
|
API Swift iOS开发
|
安全 开发者
MJRefresh源码分析
MJRefresh源码分析
180 0
MJRefresh源码分析
|
Perl 测试技术 Android开发
RxSwift、RxBlocking、RxTest初尝试
在Android开发上,RxJava已经是非常重要的组成之一。最近在用Swift开发iOS应用,考虑在架构设计上使用RxSwift。 添加依赖 # Podfile use_frameworks! target 'YOUR_TARGET_NAME' do pod 'RxSwift', '~...
1772 0
|
前端开发
[译] 实用的 MVVM 和 RxSwift
今天我们将使用 RxSwift 实现 MVVM 设计模式。对于那些刚接触 RxSwift 的人,我 在这里 专门做了一个部分来介绍。
1375 0