RxSwift + MJRefresh 打造自动处理刷新控件状态

本文是基于 iOS - RxSwift 项目实战记录 所述,如果你还未阅读过,建议你最好还先阅读一遍,并下载Demo熟悉一下 : )

LXFBiliBili

前言

MVVM的模式中,多出了ViewModel这个角色,将逻辑处理、网络请求等繁杂操作中ViewController中抽离出来,ViewController得以瘦身。 结合RxSwift架构,我们一般就会在ViewModel中定义一个input收集繁杂操作所需的信息,通过一个transform方法将input作为参数传入,进而得到一个output供controller使用。

在使用RxSwift开发时会大量的使用到这种形式,其中就包括我们的网络请求。 结合 iOS - RxSwift 项目实战记录 中所述的“MJRefresh在RxSwift中的使用”,在output中定义了一个变量

let refreshStatus = Variable<LXFRefreshStatus>(.none)

controller通过output将其进行监听,从而当值发生变化时,controller就能实时获取当前应所处的刷新状态

vmOutput.refreshStatus.asObservable().subscribe(onNext: {[weak self] status in
    switch status {
    case .beingHeaderRefresh:
        self?.tableView.mj_header.beginRefreshing()
    case .endHeaderRefresh:
        self?.tableView.mj_header.endRefreshing()
    case .beingFooterRefresh:
        self?.tableView.mj_footer.beginRefreshing()
    case .endFooterRefresh:
        self?.tableView.mj_footer.endRefreshing()
    case .noMoreData:
        self?.tableView.mj_footer.endRefreshingWithNoMoreData()
    default:
        break
    }
}).addDisposableTo(rx_disposeBag)

如果在一个项目多处使用到了这种方式,我们就可以看到弊端——重复代码,过于冗余。

难道我们每次都要在controller中进行如此操作吗?

面向协议

关于协议的内容可以看下我之前的这两篇文章 iOS - Swift 面向协议编程(一) iOS - Swift 面向协议编程(二)

总结协议的两大作用:1、规范 2、定制能力

定义协议 Refreshable

/* ============================ Refreshable ================================ */
// 需要使用 MJExtension 的控制器使用

protocol Refreshable {
    
}
extension Refreshable where Self : UIViewController {
    func initRefreshHeader(_ scrollView: UIScrollView, _ action: @escaping () -> Void) -> MJRefreshHeader {
        scrollView.mj_header = MJRefreshNormalHeader(refreshingBlock: { action() })
        return scrollView.mj_header
    }
    
    func initRefreshFooter(_ scrollView: UIScrollView, _ action: @escaping () -> Void) -> MJRefreshFooter {
        scrollView.mj_footer = MJRefreshAutoNormalFooter(refreshingBlock: { action() })
        return scrollView.mj_footer
    }
}

在controller中遵循 Refreshable 协议,通过initRefreshHeader方法或者initRefreshFooter方法给tableView或者collectionView赋予头部或尾部刷新的能力,并且书写下拉刷新时需要执行的代码

// 以下拉刷新为例
let refreshHeader = initRefreshHeader(liveCollectionView) { [weak self] in
    // 下拉后需要执行的操作 
    self?.vmOutput?.requestCommand.onNext(())
}

接下来再讲讲output,只要有网络请求的地方,就会需要需要监听请求状态,既然这样,那么可以为output定义一个协议OutputRefreshProtocol,专门用来规范必需声明的属性

/* ============================ OutputRefreshProtocol ================================ */
// viewModel 中 output使用

protocol OutputRefreshProtocol {
    // 告诉外界的tableView当前的刷新状态
    var refreshStatus : Variable<LXFRefreshStatus> {get}
}

接着让output去遵循该协议,并进行初始化刷新状态的值为.none

struct LXFLiveOutput: OutputRefreshProtocol {
    var refreshStatus: Variable<LXFRefreshStatus>
    
    let sections: Driver<[LXFLiveSection]>
    init(sections: Driver<[LXFLiveSection]>) {
        self.sections = sections
        refreshStatus = Variable<LXFRefreshStatus>(.none)
    }
}

到此为止,其实跟之前没啥两样,只是使controller更方便初始化刷新控件而已。接下来才是本文的重点。

重点

刷新的状态无非也就那么几种,下拉重载数据,上拉加载更多,请求完成时结束下拉或上拉等等。。。那我们何必要在每个controller中再去管理这等琐事?? 而至此,刷新控件的状态是由变量 refreshStatus 来决定,此时 refreshStatus 又声明在 OutputRefreshProtocol 协议中,我们何不再定义一个方法,将刷新控件的状态交给refreshStatus自己来帮我们处理呢~

extension OutputRefreshProtocol {
    func autoSetRefreshHeaderStatus(header: MJRefreshHeader?, footer: MJRefreshFooter?) -> Disposable {
        return refreshStatus.asObservable().subscribe(onNext: { (status) in
            switch status {
            case .beingHeaderRefresh:
                header?.beginRefreshing()
            case .endHeaderRefresh:
                header?.endRefreshing()
            case .beingFooterRefresh:
                footer?.beginRefreshing()
            case .endFooterRefresh:
                footer?.endRefreshing()
            case .noMoreData:
                footer?.endRefreshingWithNoMoreData()
            default:
                break
            }
        })
    }
}

这时需要我们将刷新控件的对象 header / footer 传入到方法中,实现自动控制刷新控件状态。

总结使用

一、output中遵守协议 OutputRefreshProtocol, 并初始化 refreshStatus 的值为 none

struct LXFLiveOutput: OutputRefreshProtocol {
    var refreshStatus: Variable<LXFRefreshStatus>
    
    let sections: Driver<[LXFLiveSection]>
    init(sections: Driver<[LXFLiveSection]>) {
        self.sections = sections
        refreshStatus = Variable<LXFRefreshStatus>(.none)
    }
}

二、controller 遵守协议 Refreshable,通过协议中的方法初始化刷新控件及对应的操作,并将刷新控件对象作为参数传入到自动处理状态方法中

extension LXFLiveViewController: Refreshable 
let refreshHeader = initRefreshHeader(liveCollectionView) { [weak self] in
    self?.vmOutput?.requestCommand.onNext(())
}
vmOutput?.autoSetRefreshHeaderStatus(header: refreshHeader, footer: nil).disposed(by: rx.disposeBag)

三、viewModel中根据实际情况实时更新 refreshStatus 的刷新状态

image.png

案例

协议:Refreshable.swift ViewModel:LXFLiveViewModel Controller:LXFLiveViewController

LXFBiliBili

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏潇涧技术专栏

Mou and StackEdit and Mathjax

本文记录使用Mou和Stackedit中出现的一些问题,使其能够正常渲染带数学公式的文章

851
来自专栏挖掘大数据

Cobub无码埋点关键技术实现流程(附图)

随着大数据时代的到来,数据采集也已经变的越来越重要。前端埋点作为一个比较成熟的数据接入手段被广泛应用着。目前埋点分为两种方式,有码与无码埋点。有码埋点比较容易理...

2416
来自专栏落影的专栏

iOS开发笔记(八)---- 键盘、静态库、动画、Crash定位

3799
来自专栏技术之路

Qt5 新特性

Qt 5 已经临近发布,其最大的特点就是模块化。将原来庞大的模块更细分为不同的部分,同时,一个大版本的升级,当然少不了添加、删除各个功能类。文本简单介绍 Qt5...

2988
来自专栏技术博客

Knockout.Js官网学习(简介)

最近一段时间在网上经常看到关于Knockout.js文章,于是自己就到官网看了下,不过是英文的,自己果断搞不来,借用google翻译了一下。然后刚刚发现在建立a...

982
来自专栏DannyHoo的专栏

底牌项目中上传牌谱功能之编辑标准模式

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010105969/article/de...

1083
来自专栏码生

ReactNative 面试题

今天有一个 ReactNative 的面试。 时间紧迫,临时写了几个问题,初级面试问题。 都是关键字

4793
来自专栏iOS开发攻城狮的集散地

iOS app国际化 、跳转到系统设置、iOS10通知、正则表达式

1574
来自专栏一个爱瞎折腾的程序猿

Ajax 文件上传

原文地址:http://blog.sina.com.cn/s/blog_5d64f7e3010127ns.html

2121
来自专栏娱乐心理测试

小程序完整demo

1895

扫码关注云+社区