一般咱們用KVO或者在scrollViewDidScroll代理方法中監聽ScrollView/TableView的contentOffset,好比監聽TableView的contentOffset來設置導航欄的透明度或者拉伸頂部的圖片。git
常見的姿式是在scrollViewDidScroll的代理方法中監聽scrollView.contentOffset.y,會發現有導航欄時scrollView.contentOffset.y初始值可能會等於-64,若是再手動設置tableView.contentInset這個值又會改變,這個時候就須要計算出初始偏移量,而後再算偏移的差值,要是過幾天再改下需求......從新計算github
那有沒有更舒暢的姿式? 算法
有ide
首先定義2個成員屬性,一個是監測範圍的臨界值,另外一個用來記錄scrollView.contentInset.top(重點)優化
// 監測範圍的臨界點,>0表明向上滑動多少距離,<0則是向下滑動多少距離 @property (nonatomic, assign)CGFloat threshold; // 記錄scrollView.contentInset.top @property (nonatomic, assign)CGFloat marginTop; // 這裏設值-80,即向下滑動80,-80到0就是我們重點監測的範圍 self.threshold = -80;
而後在KVO回調或者scrollViewDidScroll代理方法中加上下面的代碼。
須要理解的就是contentInset,四個值表明tableView的contentView上下左右距離邊界的值,界面有導航欄時,系統會自動將tableView的contentView到頂部的距離設爲64,即contentInset.top=64,若是導航欄透明就能夠看到有空白區域。沒有導航欄或者咱們調用self.automaticallyAdjustsScrollViewInsets = NO時,tableView的contentInset.top就會變爲0。經過下面的算法,咱們就能夠不用去刻意計算初始的偏移量,只要設置好臨界值就行,newoffsetY 就是咱們實際要監測的偏移。PS:手動設置tableView.contenInset須要在viewDidAppear中進行。ui
// 實時監測scrollView.contentInset.top, 系統優化以及手動設置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 即是咱們想監測的偏移offset.y,初始值爲0 // 向下滑動時<0,向上滑動時>0; CGFloat newoffsetY = offsetY + self.marginTop; if (newoffsetY >= self.threshold && newoffsetY <= 0) { CGFloat progress = newoffsetY/self.threshold; }
是騾子是馬,拉出來溜溜,再看看效果圖。atom
第一個頁面(首頁),上劃變透明spa
- (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]; }else if (newoffsetY > 150){ [self.navigationController.navigationBar jp_setNavigationBarBackgroundAlpha:0]; }else{ [self.navigationController.navigationBar jp_setNavigationBarBackgroundAlpha:1]; } }
第二個界面仿知乎日報的效果,一輩子X命在他的文章仿寫知乎日報 - 主頁面補遺(Part 2)用另外的方法實現過。代理
簡單說下個人思路code
- (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]; }else if (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]]; }else if (newoffsetY < HeaderFrame.origin.y){ [self.refrshView resetNavigationItemTitle:@"首頁"]; self.refrshView.hidden = NO; } }
先看效果圖,下面的scrollView翻頁後才移動上面的導航條,你會怎麼實現?
首先想到的確定是結束減速的代理方法:scrollViewDidEndDecelerating,可是滑動過快的時候是不會調用scrollViewDidEndDecelerating代理方法的,若是作過用3個界面+scrollView實現循環滾動展現圖片,那麼基本上都會碰到這麼問題。如何準確的監聽翻頁?個人解決的思路以下
// 開始減速的時候開始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/index CGFloat index = scrollView.contentOffset.x/self.screenWidth; //再四捨五入推算本該減速時的scrollView的contentOffset。即:roundf(index)*self.screenWidth] } }