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

主要介紹

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

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

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


image

image


常見的姿式是在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


iamge

第一個頁面(首頁),上劃變透明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)用另外的方法實現過。代理


來自一輩子X命


簡單說下個人思路code

  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 著做權歸做者全部,轉載請聯繫做者得到受權,並標註「簡書做者」。
相關文章
相關標籤/搜索