一般咱们用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] } }