一懂缕、前言
最近在解答[最新版]MJRefresh解析與詳細使用指導和MJRefresh實現(xiàn)刷新(使用它的Block方法)中簡友的提問顿天,淺讀了下MJRefresh的源碼
(關于源碼解讀,網上已有很多惫皱,我后續(xù)也會寫一篇我自己的解讀健田,不過今天要說的是:借鑒別人的思路,做or完善自己的事晃痴。):利用KVO在- (void)willMoveToSuperview:(UIView *)newSuperview方法調用時監(jiān)聽scrollView的contentOffset/contentSize和panGestureRecognizer的state屬性,然后做對應操作
财忽。
二倘核、開發(fā)困惑
通常,作為iOS開發(fā)人員即彪,判斷UIScrollView/UITableView/UICollectionView
的滾動情況的事笤虫,時有發(fā)生。如果每次都去實現(xiàn)delegate
方法,在我看來祖凫,有些麻煩琼蚯。除了一遍一遍的寫代理,還有一種就是建個基類惠况,但是這樣基類還是要實現(xiàn)對應的delegate
方法遭庶。
三、解決方法:給UIScrollView添加block屬性監(jiān)聽滾動
先預覽下效果
(上面紅色的是手機錄屏所致)
:
四稠屠、理清思路
- 新建一個類
PPMJRefreshComponent,類似MJRefresh中的MJRefreshComponent
峦睡,用來當做觀察者;- 既然
PPMJRefreshComponent
要觀察UIScrollView
的contentOffset
以及panGestureRecognizer
的state
,那么PPMJRefreshComponent
就要關聯(lián)當前的UIScrollView
;并且权埠,UIScrollView
要擁有一個PPMJRefreshComponent
對象(如下圖:)榨了;
PPMJRefreshComponent
觀察的結果怎么傳遞給UIScrollView
?我采用的是delegate(PPMJRefreshComponentDelegate)
,需要UIScrollView
對象遵守攘蔽;(此處不使用block是因為block嵌套block容易出問題)
UIScrollView
對象實現(xiàn)代理龙屉,并設置scrollBlock的時候觸發(fā)監(jiān)聽:(代碼如下,注釋已寫進去)
@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) {
//處理滑動到底部,繼續(xù)上滑后系統(tǒng)自動反彈而重復調用的情況
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) {
//內容不夠一個屏幕時醉途,系統(tǒng)會自動回彈,這時候記得把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的時候结蟋,觸發(fā)監(jiān)聽
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
針對上面的代碼補充說明如下:
- 注意
component
的初識與關聯(lián),一定要弄懂為啥我代碼中要用runtime
強制關聯(lián); - 注意
pp_lastContentOffsetY
的使用渔彰,它是給UIScrollView動態(tài)綁定的記錄上一次的contentOffsetY值的嵌屎,只有在滑動的時候有效推正,最終如果你放外部的話,偏移量還是和contentOffset.Y的值一樣宝惰。 -
-(void)contentOffsetBlockAction:(NSDictionary<NSKeyValueChangeKey,id> *)change
這個方法處理滑動情況植榕,但是開始下拉和上拉到底的兩種臨街狀態(tài)時的pp_lastContentOffsetY
需要特殊處理,而這個處理就放在panGestureRecognizer.state == UIGestureRecognizerStateEnded
的時候尼夺。
最后尊残,感謝MJRefresh!
文字無法描述這個過程淤堵,當時怎么想寝衫,做的時候怎么做,后來又是怎么調整的拐邪,說多了慰毅,就失去了文章的核心,所以:感興趣的最好看下代碼扎阶,有不懂的請問我汹胃,盡我之力,一起學習东臀。
2018-03-08 14:20:40 婦女節(jié)快樂着饥!感謝公司的party,此刻吃著零食喝著飲料,匆匆結文惰赋。