// 设置scrollView的滚动范围,执行该方法

主要介绍##

  1. 监测tableView垂直滚动的舒畅姿势
  2. 监测scrollView/collectionView横向滚动的正确姿势

通常我们用KVO或者在scrollViewDidScroll代理方法中监听ScrollView/TableView的contentOffset,比如监听TableView的contentOffset来设置导航栏的透明度或者拉伸顶部的图片。

图片 1JKNavigationController

常见的姿势是在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;

也就是要导航的背景在进入界面的时候隐藏,滑动到一定位置的时候,导航的背景出现,下拉的时候,头部视图跟着放大

NSLog(@"scrollViewDidEndDragging");

// 设置scrollView的额外顶部滚动区域:(UIEdgeInsetsMake是逆时针设置,上左下右)

  1. delegate

然后在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; }

是骡子是马,拉出来溜溜,再看看效果图。

第一个页面,上划变透明

- 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命在他的文章仿写知乎日报 - 主页面补遗用另外的方法实现过。

图片 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
- viewDidAppear:animated{ [super viewDidAppear:animated]; HeaderFrame = [self.tableView rectForHeaderInSection:1];}- 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; }}

图片 3JKNavigationController

先看效果图,下面的scrollView翻页后才移动上面的导航条,你会怎么实现?

图片 4iamge

首先想到的肯定是结束减速的代理方法:scrollViewDidEndDecelerating,但是滑动过快的时候是不会调用scrollViewDidEndDecelerating代理方法的,如果做过用3个界面+scrollView实现循环滚动展示图片,那么基本上都会碰到这么问题。如何准确的监听翻页?我的解决的思路如下

  1. 把原来减速后需要处理的代码整合到一个方法中,并且需传入scrollView的contentOffset,来确定当前的page/index。后面简称
  2. 整个过程中主要留意三个代理方法,拖动后 开始减速-->结束减速-->开始拖动
  3. 正常滑动速度的时候,先调用scrollViewWillBeginDecelerating再调用scrollViewDidEndDecelerating,然后调用方法A
  4. 快速滑动的时候,先调用scrollViewWillBeginDecelerating,再调用scrollViewWillBeginDragging,不会调用scrollViewWillBeginDragging。咱们可以加个flag,来判断是否减速。没减速再调用方法A
// 开始减速的时候开始self.didEndDecelerating = NO;结束减速就会置为YES,如果滑动很快就还是NO。- scrollViewWillBeginDecelerating:(UIScrollView *)scrollView{ self.didEndDecelerating = NO;}- scrollViewDidEndDecelerating:(UIScrollView *)scrollView{ self.didEndDecelerating = YES; // 调用方法A,传scrollView.contentOffset}// 再次拖拽的时候,判断有没有因为滑动太快而没有调用结束减速的方法。// 如果没有,四舍五入手动确定位置。这样就可以解决滑动过快的问题- scrollViewWillBeginDragging:(UIScrollView *)scrollView{ if (!self.didEndDecelerating) { // 先计算当期的page/index CGFloat index = scrollView.contentOffset.x/self.screenWidth; //再四舍五入推算本该减速时的scrollView的contentOffset。即:roundf*self.screenWidth] }}

文章用到的2个Demo,相关的类都已经封装好,可以直接用。

  • 优雅的搞定透明导航栏 JKNavigationController
  • 滑动导航条 JPSlideBar.GitHub

if (scrollView.contentOffset.y<-64) {

}

 

默认1.0,用来设置scrollView最少缩小比例.

}

12、

 

 

CGRect initFrame;

}

 

控制控件是否接触取消touch的事件 

CGFloat offset = (scrollView.contentOffset.y + scrollView.contentInset.top) * -1;

// 滚动视图减速完成,滚动停止时,调用该方法。一次有效滑动,只执行一次。

 

 

self.bgView.hidden=YES;

NSLog(@"%f,%f",point.x,point.y);

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView;

 

initFrame.size.height=200+offset;

NSLog(@"scrollViewDidScroll");

 

11.10 scrollView完成缩放

iOS手机QQ空间导航样式是,进入界面时,没有导航栏,随着界面的滑动,滑动到一定位置的时候,会出现导航条,而且,按钮的位置不变,一直在界面的顶部。

- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView{

指定控件是否只能在一个方向上滚动

 

2、在viewDidLoad中得到self.navigationController.navigationBar.subviews中的_UINavigationBarBackground,并设置为透明,这样就能在刚进入界面的时候没有显示导航的背景色

}else{

 

 

CGFloat offset = (scrollView.contentOffset.y + scrollView.contentInset.top) * -1;

NSLog(@"decelerate");

7.showsHorizontalScrollIndicator

 

}];

}

控制控件是否接触取消touch的事件 

 

}

// 滑动减速时调用该方法。

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView

 

-(void)scrollViewDidScroll:(UIScrollView *)scrollView

if (currentPostion - lastPosition>25) {

  1. dragging 

 

initFrame.origin.y=- offset;

}

 

// 设置scrollView允许子视图的最小缩放比例

3、设置好UIScrollView,UIScrollView的坐标需要注意下,不同的系统,默认的坐标初始位置不同,需已屏幕最左上角为原点。scrollView的下面的布局需要注意一下坐标问题。

}

 

 

4、设置好topView,即进入界面时需要显示的头部view。

- (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView{

  1. delegate

 

CGRect f    = topView.frame;

// 当pagingEnabled属性为YES时,不调用该方法

11.6 即将减速完成的时候调用。

设定滚动条的样式


//- (void)scrollRectToVisible:(CGRect)rect animated:(BOOL)animated 方法

- (void)scrollViewDidZoom:(UIScrollView *)scrollView NS_AVAILABLE_IOS(3_2);

// 设置scrollView允许子视图的最大放大比例

1、设置好导航的标题、左右边按钮

NSLog(@"scrollViewDidEndDecelerating");

    scrollView.contentInset = UIEdgeInsetsMake(100, 0, 0, 0);

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset NS_AVAILABLE_IOS(5_0);

if (scrollView.contentOffset.y<-64) {

7、

 

  1. bounces

f.size.width = scroll.frame.size.width;

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{

 

// 隐藏水平滚动条

{

// decelerate,指当我们手指离开那一瞬后,视图是否还将继续向前滚动一段距离,经过测试,decelerate=YES

 

 

self.navigationBgView.hidden=YES;

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{

8.showsVerticalScrollIndicator

 

bgView.hidden=YES;

}

 

10.maximumZoomScale

2、在viewDidLoad中得到self.navigationController.navigationBar.subviews中的_UINavigationBarBackground,并设置为透明,这样就能在刚进入界面的时候没有显示导航的背景色

//当缩放结束后,并且缩放大小回到minimumZoomScale与maximumZoomScale之间后(我们也许会超出缩放范围),调用该方法。

 

 

initFrame.origin.x=- offset /2;

}

    scrollView.scrollEnabled = NO;

默认为NO,用来设置scrollView是否开启分页.

topView.frame=initFrame;

- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale{

默认为YES,用来设置scrollView是否显示垂直滚动条.

    scrollView.bounces = NO;

[UIView animateWithDuration:0.2 animations:^{

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView{

// 设置scrollView允许子视图的最小缩放比例

 

for (UIView *view in self.navigationController.navigationBar.subviews) {

13、综上所述,实现一个简单的判断控件是在向上还是向下滑动的检测实现方式

 

 

[UIView animateWithDuration:0.2 animations:^{

intcurrentPostion=scrollView.contentOffset.y;

  1. bounces

    scrollView.minimumZoomScale = 0.8;

for (UIView *view in self.navigationController.navigationBar.subviews) {

int lastPosition;//记录上一次位置

11.9 scrollView即将开始缩放

指定控件是否只能在一个方向上滚动

[UIView animateWithDuration:0.2 animations:^{

//scrollView滚动时,就调用该方法。任何offset值改变都调用该方法。即滚动过程中,调用多次

 

11.4 即将停止拖拽的时候调用

}

- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView{

11.1当scrollView的偏移量一改变就会调用这个方法,即滚动scrollView就会调用。

5.pagingEnabled

本文由必威发布于必威-编程,转载请注明出处:    // 设置scrollView的滚动范围,执行该方法

TAG标签:
Ctrl+D 将本页面保存为书签,全面了解最新资讯,方便快捷。