本文要讨论的是相似于即刻、淘票票首页,抖音、简书我的主页这样的嵌套滚动效果,事实上网上已经有不少的相关的文章,好比:git
并且绝大多数的文章都是从如何解决手势冲突出发给出相应的解决方案,缘由是他们大多数都采用了三级 Scrollview 的解决方案,以下图github
能够看到三级 ScrollView 和 一级 ScrollView都须要在纵向滚动,因此重点要解决的就是这里的滚动冲突,具体的细节我就再也不赘述,你们还能够参考HGPersonalCenter这个项目,里面有详细的注释。app
之因此在前面给出了四个例子,是由于淘票票和简书采用的是上面提到的方案,而抖音和即刻两个则不是,而且即刻在体验上更完美,这个后面会讲到。简单粗暴的用越狱手机+Reveal验证下淘票票spa
固然经过点击状态栏看也能够粗略判断实现方式,好比淘票票在点击状态栏后视图只会滚动到子 ScrollView 的顶部而不是最外面 ScrollView 的,简书虽然滚动到最外层的顶部但效果明显不够天然,缘由就是三级 ScrollView 在纵向没有延伸到顶部。设计
抖音和即刻在点击状态栏返回到顶部的效果很是天然,因此有理由相信它们在实现上不一样于上述方案,那么抖音和即刻的实现方式具体有什么不一样?一样的用Reveal看下即刻的视图结构开发
从总体结构上来看即刻只有二级 ScrollView,因此在纵向上 ChildScrollView 会彻底接管手势,横向滚动时又由 MainScrollView 控制,这样子带来的好处在于无需关心手势冲突问题,但要实现前面提到的效果还必须处理是如下问题:rem
在这里就不给出具体的实现细节,文章后面最后有经过两种方案实现的开源库,欢迎 Star。get
前面提到的即刻和抖音采用的都是这种二级 ScrollView 的方案,但即刻在体验上更好,好比抖音的我的主页若是手指开始滚动的地方有可交互的控件(Tab栏),那么这时候滑动是会失效的,还有在切换Tab后将视图下拉滚动到顶部而后返回到以前的Tab页,抖音是直接返回到了原始的位置而即刻仍是能保留以前进度。同步
头部滚动失效解决方案it
即刻为了达到完美的效果,在每一个 ChildScrollView 顶部都添加了 HeaderView 和 MenuView,这样子做为一个总体,即便开始触摸的地方有可交互控件也能够上下滚动。而后在左右滑动的时又让ChildScrollView 内的 HeaderView 和 MenuView 隐藏,当中止滚动的时让本来在外层 ScrollView 内的 HeaderView 和 MenuView 显示。
保留进度解决方案
关于保留进度首先要作的就是判断当前 ChildScrollView 是否是处于一种特殊状态,这种状态就是 offset.y的值是否大于 HeaderView 的偏移量,而后再经过判断 ChildScrollView 当前的滚动方向,来决定是否要调整 HeaderView 和 MenuView 的位置。
对比两个方案最终的实现各有优缺点
优势:
缺点:
优势:
缺点
这里要提的是,因为方案二中 MainScrollView 并不会在纵向有滚动,因此下拉刷新必须放在 ChildViewController 内实现,但又由于 HeaderView 和 MenuView 须要根据 ChildScrollView 的偏移而移动,在配合MJRefresh时它们的偏移有明显的Bug(在本文发布前我并没深究解决方案),或许即刻也是由于这个缘由而采用上面提到的解决办法。
上面两个解决方案中的 MenuView 都设计成了交由开发者实现,由于即便集成各类样式的也难知足设计上的千奇百怪的要求,参考个人Demo就能很快实现一个本身想要的效果。