类似简书/微博/Twitter个人中心中多分页scollView的比较完美解决方案

背景

自以为完美的解决方案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的时候有效。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容

  • 1、通过CocoaPods安装项目名称项目信息 AFNetworking网络请求组件 FMDB本地数据库组件 SD...
    X先生_未知数的X阅读 15,937评论 3 118
  • 出来混,迟早都是要还的。原谅我没有在第一时间记录那些美好的瞬间。无意间翻看到手机里的照片,让我们一起回忆… ...
    范琳琳123阅读 659评论 0 0
  • 5月24号我终于跟上了队伍,早上七点到达丽江,初到丽江我就喜欢这里,可能是太早,丽江很安静,温暖的阳光倾泻在...
    莎莎西阅读 427评论 2 2
  • 伤,有身体的伤,还有心里的伤。有伤就有痛,不管是心里的还是身体上都会有痛感,区别在于一个痛在心里,一个痛在神...
    吴枫WF阅读 215评论 0 0
  • 盼望已久的寒假终于来了,因为我终于可以自己做个“小大人”了。可以自己管理自己了,合理安排生活,我的安排是这样...
    杨京瑜阅读 230评论 1 1