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 著做权归做者全部,转载请联系做者得到受权,并标注“简书做者”。
相关文章
相关标签/搜索