javanli / blog

blog
0 stars 0 forks source link

mjrefresh闲聊 #46

Open javanli opened 4 years ago

javanli commented 4 years ago

聊聊MJRefresh。

下拉刷新&上拉加载,实现起来总的来说是不太难的,基本思路就是监听contentOffset,在合适的时机调整offset/inset即可。

说起来简单,但真的要把这个能力抽象成通用性极强的基础组件,还是比较需要功力的。而MJRefresh,封装得就非常漂亮。

MJRefreshHeader为例,它其实就是个展示刷新状态的view,使用起来非常简单:

MJRefreshHeader *mjheader = [MJRefreshHeader headerWithRefreshingTarget:self refreshingAction:@selector(refreshData)];
self.tableView.mj_header = mjheader;

在set到tableview的时候,mjheader会被add上去,而在willMoveToSuperview时,会完成一系列监听等绑定逻辑。这些逻辑都藏在水面下,接口上,mjrefresh确实做到了最简洁。

另一点,mjrefresh的状态管理非常清晰。

/** 刷新控件的状态 */
typedef NS_ENUM(NSInteger, MJRefreshState) {
    /** 普通闲置状态 */
    MJRefreshStateIdle = 1,
    /** 松开就可以进行刷新的状态 */
    MJRefreshStatePulling,
    /** 正在刷新中的状态 */
    MJRefreshStateRefreshing,
    /** 即将刷新的状态 */
    MJRefreshStateWillRefresh,
    /** 所有数据加载完毕,没有更多的数据了 */
    MJRefreshStateNoMoreData
};

这种组件也确实比较适合用这种状状态抽象。MJRefresh在setState时会触发很多后续逻辑,这种方式借鉴了状态机编程的思想。

顺便聊聊状态机编程,主要思想是把程序划分为多个状态,输入都处理成状态的转移,而状态的转移又可以触发后续的逻辑。在状态比较多且状态间转移比较多时,使用状态机编程可以降低逻辑的复杂性。但是状态机编程的适用范围其实比较有限,它要求单一状态比较纯,如果一种状态下带了多余的参数,状态切换又跟这些参数强相关,就很难利用好状态机模型了。因此我们使用状态机模型,往往是在比较抽象的组件或框架底层,或者是像MJRefresh这样只利用状态机模型处理一部分逻辑,但是这种拆分往往又是比较需要水平的了。而大部分业务场景,由于其天然的状态复杂性以及业务后续变更的不确定,是不太适合状态机模型的。

推荐一个iOS状态机编程的库,TransitionKit,写得蛮不错的。

适合业务的类似状态机的模型,其实就是状态管理,客户端这种东西好像不多,web端的redux之类的就很丰富了,通常是把一个model作为一个状态,而不是一个简单的int指代状态。

扯远了,了解MJRefresh也是今年入职新公司之后的事情。当时做了个下拉刷新的吸顶黄条tip。微博好像有类似的东西,不过他们的交互有点问题,出了tip时滑动列表会有一次跳动。这个玩意儿看着简单做起来还挺麻烦的,出现这个tip条时要改变inset,tip条消失/上滑到一定位置时要悄无声息地把这个inset干掉,这里监听contentoffset时改变inset有个坑,mjrefresh里面在有些场景也会改,搞不好会死循环,我最终解决方案是在特定逻辑前后屏蔽kvo,实现其实是挺丑陋的。

就吐槽一下。