ios監聽ScrollView/TableView滾動的正確姿勢


主要介紹

  1. 監測tableView垂直滾動的舒暢姿勢
  2. 監測scrollView/collectionView橫向滾動的正確姿勢

1.監測tableView垂直滾動的舒暢姿勢

通常我們用KVO或者在scrollViewDidScroll代理方法中監聽ScrollView/TableView的contentOffset,比如監聽TableView的contentOffset來設置導航欄的透明度或者拉伸頂部的圖片。


image

image


常見的姿勢是在scrollViewDidScroll的代理方法中監聽scrollView.contentOffset.y,會發現有導航欄時scrollView.contentOffset.y初始值可能會等於-64,如果再手動設置tableView.contentInset這個值又會改變,這個時候就需要計算出初始偏移量,然后再算偏移的差值,要是過幾天再改下需求......重新計算

那有沒有更舒暢的姿勢?

首先定義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中進行。

        // 實時監測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; }

是騾子是馬,拉出來溜溜,再看看效果圖。


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]; }else if (newoffsetY > 150){ [self.navigationController.navigationBar jp_setNavigationBarBackgroundAlpha:0]; }else{ [self.navigationController.navigationBar jp_setNavigationBarBackgroundAlpha:1]; } }

第二個界面仿知乎日報的效果,一生X命在他的文章仿寫知乎日報 - 主頁面補遺(Part 2)用另外的方法實現過。


來自一生X命


簡單說下我的思路

  1. tableView的y坐標為20,然后組頭高度44,加起來剛好就是導航欄的高度。因為組頭懸停在tableView的最上方,所以y坐標為20。
  2. 在navigationBar上面添加一個高度為20的view,放在狀態欄下面,跟組頭一樣的顏色。navigationBar透明后view看上去會和組頭連接起來。
  3. 在viewDidAppear方法里面計算第一個組頭的frame,取y坐標。(最好在viewDidAppear方法里面,不然加了tableHeaderView可能會產生偏差)
  4. 然后在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]; }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; } }

2.監測scrollView/collectionView橫向滾動的正確姿勢

先看效果圖,下面的scrollView翻頁后才移動上面的導航條,你會怎么實現?


iamge

首先想到的肯定是結束減速的代理方法:scrollViewDidEndDecelerating,但是滑動過快的時候是不會調用scrollViewDidEndDecelerating代理方法的,如果做過用3個界面+scrollView實現循環滾動展示圖片,那么基本上都會碰到這么問題。如何准確的監聽翻頁?我的解決的思路如下

  1. 把原來減速后需要處理的代碼整合到一個方法中,並且需傳入scrollView的contentOffset,來確定當前的page/index。后面簡稱【方法A】
  2. 整個過程中主要留意三個代理方法,拖動后 開始減速-->結束減速-->開始拖動
  3. 正常滑動速度的時候,先調用scrollViewWillBeginDecelerating再調用scrollViewDidEndDecelerating,然后調用方法A
  4. 快速滑動的時候,先調用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/index CGFloat index = scrollView.contentOffset.x/self.screenWidth; //再四舍五入推算本該減速時的scrollView的contentOffset。即:roundf(index)*self.screenWidth] } }

 



文/溪楓狼(簡書作者)
原文鏈接:http://www.jianshu.com/p/2172cca95cdc
著作權歸作者所有,轉載請聯系作者獲得授權,並標注“簡書作者”。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM