每次读优秀的代码都是一次深入的学习,每一次模仿,都是创造的开始!编程
——QQ 316045346 欢迎交流安全
MJRefresh主要为UIScrollView,UITableView和UICollectionView添加头部和尾部刷新控件。其主要由3大块组成,类别工具,核心UIScrollView类别和头部尾部刷新组件。以下图:框架
上面示意图中列出的几个工具类别主要提供方便属性访问的功能。其主要是为了方便MJRefresh库本身的调用,固然你也能够对它进行使用。函数
UIView+MJExtension类别提供了对UIView组件位置和尺寸的快速访问方法,而且都支持快速获取和设置:工具
@property (assign, nonatomic) CGFloat mj_x; @property (assign, nonatomic) CGFloat mj_y; @property (assign, nonatomic) CGFloat mj_w; @property (assign, nonatomic) CGFloat mj_h; @property (assign, nonatomic) CGSize mj_size; @property (assign, nonatomic) CGPoint mj_origin;
UIScrollView+MJExtension提供了对UIScrollView的内容尺寸,偏移量等属性的快速访问:源码分析
@property (readonly, nonatomic) UIEdgeInsets mj_inset; @property (assign, nonatomic) CGFloat mj_insetT; @property (assign, nonatomic) CGFloat mj_insetB; @property (assign, nonatomic) CGFloat mj_insetL; @property (assign, nonatomic) CGFloat mj_insetR; @property (assign, nonatomic) CGFloat mj_offsetX; @property (assign, nonatomic) CGFloat mj_offsetY; @property (assign, nonatomic) CGFloat mj_contentW; @property (assign, nonatomic) CGFloat mj_contentH;
NSBundle+MJRefresh这个类别提供对库中资源的访问方法:布局
+ (instancetype)mj_refreshBundle; //获取箭头图片 + (UIImage *)mj_arrowImage; //获取国际化字符串 + (NSString *)mj_localizedStringForKey:(NSString *)key value:(NSString *)value; + (NSString *)mj_localizedStringForKey:(NSString *)key;
这个类别是MJRefresh库的核心,其中提供的mj_header和mj_footer两个属性用来添加头部和尾部刷新组件。这两个组件是做为子视图添加在UIScrollView上的,所以和UIScrollView的原生头尾视图都不影响。在之前版本的MJRefresh中,使用的是header和footer属性,容易产生疑惑,所以后面版本框架中都添加了mj前缀。学习
UIScrollView+MJRefresh类别在开发者设置mj_header和mj_footer属性时,将这两个组件添加为当前滚动视图的最下层子视图,为了知足某些自动加载的需求,这里面有用runtime将UITableView和UICollectionView的reload函数进行替换,这样作的目的是为了在数据加载时统计界面的元素个数。动画
MJRefreshComponent是刷新组件的基类,其中定了一些通用方法。首先,MJRefresh库的刷新组件核心思想是基于状态的,即经过状态来触发某些组件行为,例如正常的常态,下拉的pulling态,释放的refreshing态等等。开发者除了能够手动设置状态外,主要经过监听UIScrollView的偏移量等属性来改变状态。当UIScrollView有偏移量或内容尺寸的变化时,MJRefreshComponent会调用scrollViewContentOffsetDidChange函数,这个函数主要交给其子类实现。atom
MJRefreshHeader类是头部刷新组件的基类,其将刷新组件布局在UIScrollView组件的顶部,而且封装了记录上次刷新时间的功能。MJRefreshStateHeader提供了接口供开发者设置不一样状态下刷新组件所显示的文字,MJRefreshNormalHeader是一个更加上层的头部刷新组件,其状态文字是默认定义好的,而且支持国际化。MJRefreshGifHeader能够支持显示自定义刷新动画,其能够为某个状态设置一组图片。
尾部刷新组件的编写逻辑和头部刷新组件的编写逻辑基本一致,MJRefresh中的尾部刷新组件分为了两类,一类是刷新完成后自动消失的,一类是自动刷新,刷新完成后不会自动消失,只是改变状态。MFRefreshFooter与MJRefreshHeader的实现基本一致,MJRefreshBackFooter有刷新完成后自动还原的功能,MJRefreshBackNormalFooter是比较上层的封装,其显示默认的状态文案,而且支持国际化。MJRefreshBackStateFooter则能够手动设置不一样状态下刷新组件显示的文字。MJRefreshBackGifFooter用来显示自定义动画的尾部刷新组件。MJRefreshAutoFooter是自动尾部刷新组件的基类,其能够设置当尾部刷新组件出现多少比例时进行刷新(默认是彻底出现后进行刷新)。MJRefreshAutoStateFooter能够自定义其各个状态的文案。一样,也有比较上层的MJRefreshAotuNormalFooter组件,这个组件封装好了国际化的文案能够直接使用,MJRefreshAutoGifFooter组件能够显示自定动画的尾部刷新。
之因此看MJRefresh库的代码很是舒服,很大一部分源自其深刻的复用。首先MJRefreshComponent类抽象出了回调与刷新函数,而且提取出了须要子类复写的通用的布局、监听等函数,让子类的结构很是统一。MJRefreshHeader和MJRefreshFooter做为头部与尾部刷新组件的基类,抽象出了构造函数,而且实现了大部分组件与外部的布局,逻辑动做等函数。再子类则专一与实现子类自身的UI与功能。还有一个小细节,也能够看出MJRefresh对复用的追求,在setState函数的实现中,若是新的状态与旧的状态一致,则不须要作任何逻辑,全部的setState函数都须要这个逻辑,MJRefresh中采用的宏的方式进行替换,使代码变得十分简洁,示例以下:
#define MJRefreshCheckState \ MJRefreshState oldState = self.state; \ if (state == oldState) return; \ [super setState:state]; /////////// - (void)setState:(MJRefreshState)state { MJRefreshCheckState // 设置状态文字 self.stateLabel.text = self.stateTitles[@(state)]; }
不少时候,咱们在执行block的时候都会先检查下这个block是否为nil,下面是咱们经常使用的代码:
if (block) { block(); }
在MJRefresh中有使用问号冒号的方式来代替if语句,以下:
- (void)executeReloadDataBlock { ! self.mj_reloadDataBlock ? : self.mj_reloadDataBlock(self.mj_totalDataCount); }
这个表达式初看会有一些疑惑,其实?:的做用是返回一个值,若是?:前的表达式为nil的话,则会返回?:后面的值,一样,若是?:前面的表达式不为nil的话,则直接返回,不会执行到后面的表达式,上面的写法其实和第一种if语句的做用彻底一致。