ios監(jiān)聽ScrollView/TableView滾動的正確姿勢

主要介紹

監(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]

? ? }

}

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容