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



相关文章
|
存储 编译器 Swift
Swift笔记:Swift中的扩展语法
Swift笔记:Swift中的扩展语法
457 1
|
人工智能 自动驾驶 安全
人工智能浪潮下的伦理困境与未来展望
在AI技术飞速发展的今天,我们站在了一个新的历史节点上。本文将探讨人工智能带来的伦理挑战,并展望未来可能的发展路径。我们将从AI技术的本质出发,分析其对社会、工作和人际关系的影响,进而深入讨论如何平衡技术进步与人类价值的关系,最后提出对未来AI发展的展望和建议。
721 2
|
前端开发 JavaScript 搜索推荐
构建简易天气预报应用
【8月更文挑战第31天】在这篇文章中,我们将一起踏上制作一个简易天气预报应用的旅程。不同于常规的技术文章摘要,这里我们直接潜入主题的核心——如何从零开始,利用HTML、CSS和JavaScript构建一个功能完备的天气预报工具。我们会探索API的使用,理解异步编程概念,并实现一个响应式设计的用户界面。准备好迎接代码和创意的结合,让我们动手实践,共同打造属于你的天气小助手!
|
存储 Swift
RxSwift+MVVM项目实战-多分组UITableView结合RxDataSources的使用
RxSwift+MVVM项目实战-多分组UITableView结合RxDataSources的使用
726 0
技术经验分享:iOS_MJRefrash的详解以及使用
技术经验分享:iOS_MJRefrash的详解以及使用
|
持续交付 开发工具 Swift
【Swift开发专栏】Swift与第三方库和框架的集成
【4月更文挑战第30天】本文探讨了Swift中集成第三方库和框架的策略,包括选择有功能需求、社区支持、丰富文档和合适许可证的库。集成步骤涉及使用CocoaPods等工具安装,`import`导入库,遵循错误处理和性能优化。建议遵循代码组织、单一职责原则,做好错误处理和日志记录,使用版本控制和CI/CD,以提升项目稳定性和用户体验。
518 0
|
存储 前端开发
RxSwift+MVVM项目实战-MVVM架构介绍以及实战初体验
RxSwift+MVVM项目实战-MVVM架构介绍以及实战初体验
961 0
|
存储 自然语言处理 算法
【软件设计师—基础精讲笔记6】第六章 结构化开发方法
【软件设计师—基础精讲笔记6】第六章 结构化开发方法
1218 0
|
JSON 数据处理 API
在Swift中,数据处理和网络请求
在Swift中,数据处理和网络请求
394 4