@chinese-ppmt
2018-04-16T05:16:26.000000Z
字数 3942
阅读 1806
runtime
最近在解答[最新版]MJRefresh解析与详细使用指导和MJRefresh实现刷新(使用它的Block方法)中简友的提问,浅读了下MJRefresh的源码
(关于源码解读,网上已有很多,我后续也会写一篇我自己的解读,不过今天要说的是:借鉴别人的思路,做or完善自己的事。):利用KVO在- (void)willMoveToSuperview:(UIView *)newSuperview方法调用时监听scrollView的contentOffset/contentSize和panGestureRecognizer的state属性,然后做对应操作
。
通常,作为iOS开发人员,判断UIScrollView/UITableView/UICollectionView
的滚动情况的事,时有发生。如果每次都去实现delegate
方法,在我看来,有些麻烦。除了一遍一遍的写代理,还有一种就是建个基类,但是这样基类还是要实现对应的delegate
方法。
先预览下效果
(上面红色的是手机录屏所致)
:
- 新建一个类
PPMJRefreshComponent,类似MJRefresh中的MJRefreshComponent
,用来当做观察者;- 既然
PPMJRefreshComponent
要观察UIScrollView
的contentOffset
以及panGestureRecognizer
的state
,那么PPMJRefreshComponent
就要关联当前的UIScrollView
;并且,UIScrollView
要拥有一个PPMJRefreshComponent
对象(如下图:);
PPMJRefreshComponent
观察的结果怎么传递给UIScrollView
?我采用的是delegate(PPMJRefreshComponentDelegate)
,需要UIScrollView
对象遵守;(此处不适用block是因为block嵌套block容易出问题)
UIScrollView
对象实现代理,并设置scrollBlock的时候触发监听:(代码如下,注释已写进去)
@implementation UIScrollView (ScrollBlock)
#pragma mark --- PPMJRefreshComponentDelegate
-(void)scrollViewContentOffsetDidChange:(NSDictionary<NSKeyValueChangeKey,id> *)change{
[self contentOffsetBlockAction:change];
}
-(void)scrollViewPanStateDidChange:(NSDictionary<NSKeyValueChangeKey,id> *)change{
[self panGestureRecognizerStateAction:change];
}
-(void)contentOffsetBlockAction:(NSDictionary<NSKeyValueChangeKey,id> *)change
{
//这个属性字面理解意思为:正在拖动。实际上是:scrollView是否滚动了,只要不是最开始初始化的时候设置的位置,就为YES。
if (!self.isDragging) {
return;
}
//【注意】此处要特别注意,如果设置contentInset的话,要给pp_lastContentOffsetY赋值为insetT的初始值
if (!self.pp_lastContentOffsetY) {
[self setupInitializeOffsetY];
}
//获取当前的contentOffsetY
CGFloat currentContentOffsetY = self.pp_FSB_offsetY;
//如果前后的contentOffsetY值相同,就不做处理
CGFloat lastContentOffsetY = [self.pp_lastContentOffsetY floatValue];
if (currentContentOffsetY == lastContentOffsetY) {
return;
}
//是否是向上滑,初始值为NO
BOOL isToUp = NO;
//向上滑动
if (currentContentOffsetY > lastContentOffsetY) {
//处理滑动到底部,继续上滑后系统自动反弹而重复调用的情况
if (currentContentOffsetY+self.pp_h > self.pp_FSB_contentH) {
return;
}
isToUp = YES;
}else{
//向下滑动
//处理已经最上面了仍然下拉而反弹时,反复调用
if (currentContentOffsetY <= self.pp_FSB_insetT) {
return;
}
}
//给pp_lastContentOffsetY绑定值
objc_setAssociatedObject(self, @selector(pp_lastContentOffsetY), [NSNumber numberWithFloat:currentContentOffsetY], OBJC_ASSOCIATION_RETAIN);
//是否超过一个屏幕
BOOL isInOneScreen = (self.pp_FSB_insetT+self.pp_FSB_contentH <= self.pp_h);
if (self.pp_scrollBlock) {
self.pp_scrollBlock(currentContentOffsetY, isToUp,isInOneScreen);
}
}
-(void)panGestureRecognizerStateAction:(NSDictionary<NSKeyValueChangeKey,id> *)change
{
if (self.panGestureRecognizer.state == UIGestureRecognizerStateEnded) {
//内容不够一个屏幕时,系统会自动回弹,这时候记得把pp_lastContentOffsetY重新设置一下
if (self.pp_FSB_insetT+self.pp_FSB_contentH <= self.pp_h) {
[self setupInitializeOffsetY];
}else{
//超过一个屏幕,这时候下拉,当松开的时候要把pp_lastContentOffsetY重新设置一下
if (self.pp_FSB_offsetY < self.pp_FSB_insetT) {
[self setupInitializeOffsetY];
}
}
}
}
#pragma mark --- 初始化contentOffsetY的值
-(void)setupInitializeOffsetY{
CGFloat currentContentOffsetY = -self.pp_FSB_insetT;
objc_setAssociatedObject(self, @selector(pp_lastContentOffsetY), [NSNumber numberWithFloat:currentContentOffsetY], OBJC_ASSOCIATION_RETAIN);
}
-(void)setPp_scrollBlock:(PPUIScrollViewScrollBlock)pp_scrollBlock
{
//在设置scrollBlock的时候,触发监听
self.pp_component.delegate = self;
objc_setAssociatedObject(self, @selector(pp_scrollBlock), pp_scrollBlock, OBJC_ASSOCIATION_RETAIN);
}
-(PPUIScrollViewScrollBlock)pp_scrollBlock
{
return objc_getAssociatedObject(self, _cmd);
}
@end
针对上面的代码补充说明如下:
1. 注意component
的初识与关联,一定要弄懂为啥我代码中要用runtime
强制关联;
2. 注意pp_lastContentOffsetY
的使用,它是给UIScrollView动态绑定的记录上一次的contentOffsetY值的,只有在滑动的时候有效,最终如果你放外部的话,偏移量还是和contentOffset.Y的值一样。
3. -(void)contentOffsetBlockAction:(NSDictionary<NSKeyValueChangeKey,id> *)change
这个方法处理滑动情况,但是开始下拉和上拉到底的两种临街状态时的pp_lastContentOffsetY
需要特殊处理,而这个处理就放在panGestureRecognizer.state == UIGestureRecognizerStateEnded
的时候。
最后,感谢MJRefresh!
文字无法描述这个过程,当时怎么想,做的时候怎么做,后来又是怎么调整的,说多了,就失去了文章的核心,所以:感兴趣的最好看下代码,有不懂的请问我,尽我之力,一起学习。
2018-03-08 14:20:40 妇女节快乐!感谢公司的party,此刻吃着零食喝着饮料,匆匆结文。