美文网首页iOS 控件定制iOS高仿源码iOS学习
类似简书/微博/Twitter个人中心中多分页scollView

类似简书/微博/Twitter个人中心中多分页scollView

作者: FindCrt | 来源:发表于2017-06-12 22:25 被阅读687次

背景

自以为完美的解决方案demo在此-TFMultiTabScrollView

这种界面效果需要一大段话来描述,也没有专门的名词,简单说,就是简书APP的个人简介的样式

简书样式

同样也是微博/Twitter的个人中心样式。总结下特点就是:

  • 一个头部界面+一个分页栏+有N个内容分页
  • 然后每一页可以各自上下滑动,同时可以横向滑动切换分页

那么这个和网易新闻的界面有什么区别?看上去好像一样。
重点就在这个头部界面,头部上下滚动也是可以带动内容上下,而头部横向滑动却不会带动内容分页切换

我找了下目前的一些方案,发现并没有很完美的,比如嵌套UIScrollview的滑动冲突解决方案。这也是一开始我想的,似乎大家很容易走到嵌套scrollView的路线上,而这条路似乎是个死胡同。

我的方案

层级图

界面结构示例 view数据层次
  • 横向滑动用来切换分页的scrollView大小占满整个界面,而不只是头部以下的位置
  • N个分页的scrollView放在横向的ScrollView上
  • 头部放在当前显示的那个分页ScrollView上

这样做可以达到:

  • 横向滚动可以切换分页
  • 头部上下滑动可以带动当前分页内容上下移动,因为头部就在内容分页的scrollView上

但是还有几个问题需要解决:

  1. 分页scrollView的内容会被头部遮挡一部分
  2. 横向切换分页后,新的分页头部是不是没有了?还是需要用3个头部?但是用3个是不是切换起来会很难看?
  3. 头部横向滑动时,会带动分页横向切换,这个效果是不需要的

解决:

  1. 调整分页scrollView的contentInsert.top,让顶部内容空出
  2. 这个问题是关键,当初就是想到这里而否定了,而没有走下去。解决方法就是:
  • 在横向滑动开始的时候,把头部从分页内容scrollView(橙色的)上拿下来,放到横向滑动的scrollView(绿色的)上,这时头部就覆盖在橙色scrollView上。
  • 然后根据滑动的contentOffset.x不断调整头部的frame,让它看起来没有动
  • 等到滑到目标分页时,再把头部又放回到当前的内容ScrollView上。

整个过程里,视觉上看起来好像头部视图一直没动。这效果就达到了。而且因为头部视图又放到了当前的分页内容ScrollView上,所以之前的效果继续保持。

  1. 这是手势冲突的问题,自定义头部视图的view,然后添加一个UIPanGestureRecognizer手势,然后实现手势冲突的方法:
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
    
    //_tabContainer是横向滑动的scrollView
    if (otherGestureRecognizer.view == _tabContainer) {
        return NO;
    }
    
    return YES;
}

_tabContainer是横向滑动的scrollView,如果是和头部的pan手势和横向滑动ScrollView的手势冲突了,就返回NO。因为头部视图在上层,所以它的首先优先了。这样就把横向滑动的手势给屏蔽了。

这么做只是为了空白的头部视图不会触发横向内容切换,如果在头部视图加入更多控件,比如再加一个UIScrollView,它本身是可以自动屏蔽横向ScrollView(绿色)的手势的。

这样做就避开了scrollView嵌套滑动手势的问题

到这主要的问题就解决了,剩下的就是分页内容ScrollView上下滑动时,头部的悬浮问题了。

使用KVO检测分页scrollView的contentOffset,然后:

  • 在向上下滑的时候,头部跟随上下滑,这里什么都不需要处理,因为把头部放到了scrollView上,这是自带效果。
  • 当tab分页栏,就是简书例子里“动态-文章-更多”那一栏,顶到了视图的顶部后,不断调整头部的frame.y让他看起来不动
  • 当头部完全显示出来后,也不断调整frame.y让它看起来不动

额外效果

  • 简书、微博和Twitter的效果是同一种,其实还有另一种类似的,就是美团的外卖商店界面,区别就是:美团这里是内容只要上下滑动,头部都是跟随动的,而简书微博这边是只有内容视图滑到顶部,也就是内容的顶接住了头部视图的顶的时候,才能拖动头部。在逻辑上,美团那种跟Safari的地址栏是一个感觉。
    为了区分这两种效果,设置了一个属性moveHeaderOnlyContentTop,YES时就是简书微博效果,NO就是美团外卖商店效果。默认YES。

  • 有些分页内容可能很少,导致tab分页栏滑不到顶部,而如果其他分页可以滑到顶部,这时就有一个问题,tab栏在顶部的时候,切换到内容不足的页面,就会导致tab分页栏刷的一下掉下来。这效果很不好。所以我加了一个属性autoFillContent。如果YES,则计算内容高度,设置contentInsert.bottom,在scrollView的底部增加一段空白。这样内容不足时,tab分页栏依然可以到顶部。默认YES。

  • 有时tab分页栏不想直接贴住视图的顶部,比如有一个导航栏效果,内容往上滑的时候,导航栏出来,往下滑时候呢,导航栏渐变消失。这时要给导航栏留出空位,tab分页栏就不能直接挨着顶部,所以我加了一个属性topSpace,这个是用来调控tab栏和顶部的间距的。默认0。

不完美的尝试

1. 三层嵌套scrollView

这种界面结构,很容易想到的就是3个scrollView嵌套:一个大的竖向scrollView----横向分页scrollView----每个分页自身的scrollView。最外层的竖向scrollView为了是头部能够滑动,所以说这个头部才是这里的症结啊!但这种结构带来的是两个竖向的scrollView的滑动切换问题:

  • 当tab分页栏滑到定后,要滑动内部的scrollView
  • 到头部除了tab栏还有更多显示出来时,要滑动外部的scrollView来带动头部。

那么为什么不一直滑动外面的scrollView呢?这其实也是一种方案,只要把内容视图的内容完全的展开,然后只滑动外层的scrollView来切换显示内容。不好的是这样就没法使用tableView的重用功能了,比如你有很多的cell,像简书的动态那一栏。但分页是可以做的,只要加载新的内容后在继续全部展开。但这个不完美的瑕疵让人不爽。

一开始我以为scrollView的哪个滑动是hitView的问题,但后来发现scrollView的滑动是它自带的pan手势来控制滑动的。手势一旦识别了,就不会再识别,除非是新的触碰,这就是这种方案为什么要放手再重新滑动才能内外scrollView切换。

如果scrollView的滑动不是它自带的手势处理,而是一个统一的手势,而scrollView只要接受来自这个统一的手势的滑动数据就好了。这样只需要把数据的输出调整成另一个scrollView就可以完成完美的滑动切换。这里就牵扯到一点设计的问题了。

2. 模拟滚动的头部视图

这种方案是:
整体的父视图 ---- 横向的scrollView ---- 分页的竖向scrollView
整体的父视图 ----- 头部

也就是头部视图和横向的scrollView是同一个层级上,然后头部在上面覆盖。

这样要要解决的关键问题就是:头部上竖向滑动,要带动分页内容色scrollView滑动。而横向滑动啊,点击啊之类的都没有问题了,因为它已经不再嵌套scrollView的层级里了。

所以我自定义了一个头部(demo里的TFScrollSimulateView),然后给它加了pan手势来模拟scrollView的滚动,其实手指拖动是很好实现的,头部的手势拖动了多少就修改scrollView的contentOffset多少,麻烦的是:

  • 手指快速的一滑,手指离开后,scrollView还要继续滚动,而且要速度越来越慢。
  • scrollView拉倒边缘时的弹簧效果

所以我做了一个计时器,在手指离开后,不断的计算滑动的距离,而pan手势是有velocityInView:方法可以取得速度。这样其实基本可以解决问题了,但有个不完美的是scrollView减速的公式只能猜:
module -= KTimerInterval * _friction * (10000 + module * module); //阻力和速度平方成正比,速度减去a*t

但是后来在一个项目里找到了相关公式:弹簧公式

弹簧公式
3. 用图片欺骗

其实如果不需要横向滑动切换分页的效果,只能点击切换的话,这个问题的难度直线下降,因为这样就不需要横向的scrollView了,内外层的scrollView可以合成一个,就完全不存在什么嵌套的问题了。

但是如果一定要有横向切换的效果呢?可以在开始横向滑动的时候,做两张图片覆盖在内容部分。手指滑动的时候,是图片在切换,但是这个图片长得跟分页的scrollView一样,让人看起来好像是分页内容切换了一样。

  • 使用core graphic的截图方法,在刚开始滑动的时候,把当前分页scrollView的界面截取下来,然后覆盖到相同位置。(就跟你把别人的桌面截一张图,然后做成背景,再把桌面图标全隐藏,看起来好像没变-_-)
  • 把之前的内容图片摆在左右,然后滑动切换时,实际是这两个图片在切换。

这个具体没去实现,只是想法,这种欺骗眼睛的手法是挺有意思的。

2017.6.28更新


修改了问题:

  1. 分页scrollView在滑动过程中会添加新的子视图,导致遮住头部,比如UITableViewsectionHeader.
  2. _currentVisableHeaderH没有初始值导致滑动出错
  3. 分页切换时,如果频繁点击会导致界面闪烁

完善功能:

  1. 在不需要自动扩充底部内容的时候,有可能某个分页内容很少,导致头部滑动不到顶部,在切换的时候会出现“突然掉下来”的现象,现在改成动画,体验更好些。
  2. 做好了某个分页滑动,其他分页同步滑动内容的处理。能够达到从A分页离开时,如果你已经滑动到了第10行是贴着头部视图的底部的,在其他分页移动了头部视图后,A分页的内容也会跟随移动,保证回去时还是第10行贴着头部。但是这个只有在moveHeaderOnlyContentTopNO的时候有效。

相关文章

网友评论

  • Beyond_JG:然而并不完美,你的demo最后一个tableview根本不能向上滚动
    FindCrt:最后一个本身内容高度不够当然不能滚上去,autoFillContent设为YES就会补充不足的空间。虽然还有不少bug,但你说的这条不是。
  • JeffWei:给了你颗星,但是发现其实还是没解决切换标签后切换后切换后的标签重置了的问题...,比如第一个标签滑动到某行,切换到第二个标签,再滑动回去的时候第一个标签会重置。。。e
    JeffWei:@find_1991 我说的效果,应该是之前标签停留在哪个部分就是哪个部分。就是:滑动还是需要跟着滑动,但是始终要保持之前看到哪一行在最上面,切过来还是那样(这一行和头部肯定是贴着的),想了好久没想到解决办法。。。
    FindCrt:你是说切回来时,之前的页面内容又到顶部了吗?如果你在某个分页把头部拉出来了一点,其他分页都会回到顶部,因为只有内容在顶部的时候才能够拖动头部,如果不同步其他页面,其他页面就会划不动头部了。你可以把moveHeaderOnlyContentTop设成NO看一下。这种时候是不会同步调整其他分页的。
  • 摸着_石头过河:楼主,您好!我在这个方法-(UIScrollView *)tabScrollviewForMultiTabScrollView:(TFMultiTabScrollView *)multiTabScrollView atTabIndex:(NSInteger)index中将子视图全部改成_moviesTableView时,左右滑动HeaderView不见了?
    FindCrt:_moviesTableView只是一个对象,它只能占一个内容位置,实际上它被放到了第三页内容上。先是放到第一页,然后获取第二页内容的时候又会被放到第二页,这样最后只在第三页上。因为没有内容视图,所以HeaderView就没有父视图看不见了。

    每个分页内容要是不同的对象。一般你也不会用同样的对象吧,那就没必要拆成不同分页了。

本文标题:类似简书/微博/Twitter个人中心中多分页scollView

本文链接:https://www.haomeiwen.com/subject/boqrqxtx.html