主要介紹
監(jiān)測tableView垂直滾動的舒暢姿勢
監(jiān)測scrollView/collectionView橫向滾動的正確姿勢
1.監(jiān)測tableView垂直滾動的舒暢姿勢
通常我們用KVO或者在scrollViewDidScroll代理方法中監(jiān)聽ScrollView/TableView的contentOffset,比如監(jiān)聽TableView的contentOffset來設置導航欄的透明度或者拉伸頂部的圖片。
image
image
常見的姿勢是在scrollViewDidScroll的代理方法中監(jiān)聽scrollView.contentOffset.y,會發(fā)現有導航欄時scrollView.contentOffset.y初始值可能會等于-64,如果再手動設置tableView.contentInset這個值又會改變,這個時候就需要計算出初始偏移量,然后再算偏移的差值,要是過幾天再改下需求......重新計算
那有沒有更舒暢的姿勢??
有
首先定義2個成員屬性,一個是監(jiān)測范圍的臨界值,另一個用來記錄scrollView.contentInset.top(重點)
// 監(jiān)測范圍的臨界點,>0代表向上滑動多少距離,<0則是向下滑動多少距離@property (nonatomic,assign)CGFloat threshold;// 記錄scrollView.contentInset.top@property (nonatomic,assign)CGFloat marginTop;// 這里設值-80,即向下滑動80,-80到0就是咱們重點監(jiān)測的范圍self.threshold = -80;
然后在KVO回調或者scrollViewDidScroll代理方法中加上下面的代碼。
需要理解的就是contentInset,四個值代表tableView的contentView上下左右距離邊界的值,界面有導航欄時,系統(tǒng)會自動將tableView的contentView到頂部的距離設為64,即contentInset.top=64,如果導航欄透明就可以看到有空白區(qū)域。沒有導航欄或者我們調用self.automaticallyAdjustsScrollViewInsets = NO時,tableView的contentInset.top就會變?yōu)?。通過下面的算法,我們就可以不用去刻意計算初始的偏移量,只要設置好臨界值就行,newoffsetY 就是我們實際要監(jiān)測的偏移。PS:手動設置tableView.contenInset需要在viewDidAppear中進行。
// 實時監(jiān)測scrollView.contentInset.top, 系統(tǒng)優(yōu)化以及手動設置contentInset都會影響contentInset.top。if (self.marginTop !=self.scrollView.contentInset.top) {self.marginTop =self.scrollView.contentInset.top;? ? ? ? }//CGFloat offsetY = [change[@"new"] CGPointValue].y;CGFloat offsetY = scrollView.contentOffset.y;// newoffsetY 便是我們想監(jiān)測的偏移offset.y,初始值為0// 向下滑動時<0,向上滑動時>0;CGFloat newoffsetY = offsetY +self.marginTop;if (newoffsetY >=self.threshold && newoffsetY <=0) {CGFloat progress = newoffsetY/self.threshold;
? ? ? ? }
是騾子是馬,拉出來溜溜,再看看效果圖。
iamge
第一個頁面(首頁),上劃變透明
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{if (self.marginTop != scrollView.contentInset.top) {self.marginTop = scrollView.contentInset.top;? ? }CGFloat offsetY = scrollView.contentOffset.y;CGFloat newoffsetY = offsetY +self.marginTop;// 臨界值150,向上拖動時變透明if (newoffsetY >=0 && newoffsetY <=150) {? ? ? ? [self.navigationController.navigationBar jp_setNavigationBarBackgroundAlpha:1- newoffsetY/150];? ? }elseif (newoffsetY >150){? ? ? ? [self.navigationController.navigationBar jp_setNavigationBarBackgroundAlpha:0];? ? }else{? ? ? ? [self.navigationController.navigationBar jp_setNavigationBarBackgroundAlpha:1];
? ? }
}
第二個界面仿知乎日報的效果,一生X命在他的文章仿寫知乎日報 - 主頁面補遺(Part 2)用另外的方法實現過。

來自一生X命
簡單說下我的思路
tableView的y坐標為20,然后組頭高度44,加起來剛好就是導航欄的高度。因為組頭懸停在tableView的最上方,所以y坐標為20。
在navigationBar上面添加一個高度為20的view,放在狀態(tài)欄下面,跟組頭一樣的顏色。navigationBar透明后view看上去會和組頭連接起來。
在viewDidAppear方法里面計算第一個組頭的frame,取y坐標。(最好在viewDidAppear方法里面,不然加了tableHeaderView可能會產生偏差)
然后在scrollViewDidScroll方法實現導航欄的透明以及切換titleView
- (void)viewDidAppear:(BOOL)animated{? ? [super viewDidAppear:animated];? ? HeaderFrame = [self.tableView rectForHeaderInSection:1];}- (void)scrollViewDidScroll:(UIScrollView *)scrollView{if (self.marginTop != scrollView.contentInset.top) {self.marginTop = scrollView.contentInset.top;? ? }CGFloat offsetY = scrollView.contentOffset.y;CGFloat newoffsetY = offsetY +self.marginTop;if (newoffsetY >=0 && newoffsetY <=150) {? ? ? ? [self.navigationController.navigationBar jp_setStatusBarBackgroundViewAlpha:newoffsetY/150];? ? }elseif (newoffsetY >150){? ? ? ? [self.navigationController.navigationBar jp_setStatusBarBackgroundViewAlpha:1];? ? }if (newoffsetY >= HeaderFrame.origin.y) {self.refrshView.hidden =YES;? ? ? ? [self.navigationController.navigationBar setBackgroundColor:[[UIColor purpleColor] colorWithAlphaComponent:0]];? ? }elseif (newoffsetY < HeaderFrame.origin.y){? ? ? ? [self.refrshView resetNavigationItemTitle:@"首頁"];self.refrshView.hidden =NO;
? ? }
}
2.監(jiān)測scrollView/collectionView橫向滾動的正確姿勢
先看效果圖,下面的scrollView翻頁后才移動上面的導航條,你會怎么實現?

首先想到的肯定是結束減速的代理方法:scrollViewDidEndDecelerating,但是滑動過快的時候是不會調用scrollViewDidEndDecelerating代理方法的,如果做過用3個界面+scrollView實現循環(huán)滾動展示圖片,那么基本上都會碰到這么問題。如何準確的監(jiān)聽翻頁?我的解決的思路如下
把原來減速后需要處理的代碼整合到一個方法中,并且需傳入scrollView的contentOffset,來確定當前的page/index。后面簡稱【方法A】
整個過程中主要留意三個代理方法,拖動后 開始減速-->結束減速-->開始拖動
正常滑動速度的時候,先調用scrollViewWillBeginDecelerating再調用scrollViewDidEndDecelerating,然后調用方法A
快速滑動的時候,先調用scrollViewWillBeginDecelerating,再調用scrollViewWillBeginDragging,不會調用scrollViewWillBeginDragging。咱們可以加個flag,來判斷是否減速。沒減速再調用方法A
// 開始減速的時候開始self.didEndDecelerating = NO;結束減速就會置為YES,如果滑動很快就還是NO。- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView{self.didEndDecelerating =NO;}- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{self.didEndDecelerating =YES;// 調用方法A,傳scrollView.contentOffset}// 再次拖拽的時候,判斷有沒有因為滑動太快而沒有調用結束減速的方法。// 如果沒有,四舍五入手動確定位置。這樣就可以解決滑動過快的問題- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{if (!self.didEndDecelerating) {// 先計算當期的page/indexCGFloat index = scrollView.contentOffset.x/self.screenWidth;//再四舍五入推算本該減速時的scrollView的contentOffset。即:roundf(index)*self.screenWidth]
? ? }
}