随着APP承载的业务愈来愈多,一个页面显示的信息也愈来愈多,须要为不一样的业务导流。主流的平台APP,诸如:淘宝、京东、转转、盒马、还有各种社交APP的我的主页,都须要在页面顶部展现核心业务数据,在底部分标签显示各个子业务列表数据。随着大屏手机的普及,若是只能经过点击顶部标签切换列表,对于使用场景最高的单手操做就很麻烦了。因此,为了用户更好的交互,须要支持子列表左右滚动切换的功能。这样就出现了UIScrollView嵌套滚动的场景了。既须要主列表上下滚动,也须要子列表左右滚动。git
为了解决嵌套滚动的问题,如今网上已经有许多解决方案了。包括笔者我,也开源了一个JXPagingView库,目前已经有1100 stars,获得许多朋友的承认。 主要支持如下特性:github
感兴趣的能够了解一下JXPagingView的原理bash
基于现有的原理,有一个小问题:当用户在顶部header用力往上滑动的时候,当分类控制器滚动到顶部的时候,会忽然停住,列表不会接着惯性继续滚动。你们能够打开【京东】APP,目前(版本号:8.3.4)首页的效果就是如此,你们能够体验一下,就明白我说的是什么意思了。markdown
如何才能像淘宝首页那样,可让子列表接着滚动呢?直到看到了转转首页,经过上手体验以后,发现了一个全新的方案。PS:不知道转转APP作了什么操做,简单的逆向不起做用,视图层级都看不到。因此,这个方案都是靠本身猜想加实践折腾出来的。oop
JXPagerSmoothView Github地址,点击立马体验spa
能够清楚的看到顶部pagerHeader用力往上滚动以后,下方列表会继续滚动的,并且滚动的速度、阻尼都是系统自带的。由于上下滚动的时候,就只是在操做一个列表,天然不会有手势冲突之类的问题,看了下面的原理解析就明白了。自定义的pagerHeader只是用一个简单的TableView做为示例,你能够用任何复杂的视图、UICollectionView等代替。代理
此方案原理很是简单,没有复杂的手势处理,只须要处理好各类边界状况便可。code
默认状况pagerHeaderContainerView
被addSubview到当前的列表UIScrollView上面,pagerHeaderContainerView
就是顶部pagerheader(核心业务视图区域)和pinHeader(悬浮分类控制器区域)的容器视图。这样子,列表上下滑动就只是在操做单个列表ScrollView,不会有滚动忽然被中断的状况。视图层级以下:orm
当列表在左右切换的时候、列表向上滚动到pinHeder悬浮的时候,pagerHeaderContainerView
被addSubview到JXPagerSmoothView上面,也就是脱离了列表scrollView,达到固定在顶部的效果。视图层级以下:get
总结:就是在不断切换pagerHeaderContainerView
的父视图,达到淘宝、转转首页的效果。是否是原理很简单?固然使用的代码也很简单!
JXPagerSmoothView
self.pager = [[JXPagerSmoothView alloc] initWithDataSource:self];
[self.view addSubview:self.pager];
复制代码
pagerHeader
和pinHeader
self.categoryView = [[JXCategoryTitleView alloc] init]; self.categoryView.titles = @[@"能力", @"爱好", @"队友"]; self.categoryView.contentScrollViewClickTransitionAnimationEnabled = NO; self.pagerHeader = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"lufei.jpg"]]; 复制代码
JXPagerSmoothViewDataSource
代理方法- (CGFloat)heightForPagerHeaderInPagerView:(JXPagerSmoothView *)pagerView {
return 300;
}
- (UIView *)viewForPagerHeaderInPagerView:(JXPagerSmoothView *)pagerView {
return self.pagerHeader;
}
- (CGFloat)heightForPinHeaderInPagerView:(JXPagerSmoothView *)pagerView {
return 50;
}
- (UIView *)viewForPinHeaderInPagerView:(JXPagerSmoothView *)pagerView {
return self.categoryView;
}
- (NSInteger)numberOfListsInPagerView:(JXPagerSmoothView *)pagerView {
return self.categoryView.titles.count;
}
- (id<JXPagerSmoothViewListViewDelegate>)pagerView:(JXPagerSmoothView *)pagerView initListAtIndex:(NSInteger)index {
SmoothListViewController *listVC = [[SmoothListViewController alloc] init];
return listVC;
}
复制代码
JXPagerSmoothViewListViewDelegate
代理方法SmoothListViewController
类实现JXPagerSmoothViewListViewDelegate
代理方法
- (UIScrollView *)listScrollView {
return self.tableView;
}
- (UIView *)listView {
return self.view;
}
复制代码
经过示例代码能够看到整个逻辑简单、清晰,和使用UITableView
同样,只须要实现对应的代理方法便可,根本不须要操心页面的交互逻辑。真正的作到了高内聚低耦合、职责分离等原则。
可是有几个点须要注意:
contentInset
属性,内部经过设置contentInset
来添加pagerHeaderContainerView
;SmoothCustomPagerHeaderViewController
类;JXPagerView
和JXPagerSmoothView
的区别,并选择适合本身需求的类;JXPagerSmoothView
在1.2.1及以上版本才有,请使用最新版本;JXPagingSmoothView
;JXPagerSmoothView
Github地址JXPagerSmoothView Github地址,点击立马体验
JXPagerSmoothView
的实现文件只有300行代码左右,须要深刻研究的朋友,相信花点功夫就能看懂。这样子之后业务上面有任何特殊要求时,均可以本身实现。只要掌握了原理,就不怕需求的变化。
有任何建议或疑问,能够留言、提Issues,我都会第一时间回复你!
感谢你的阅读,喜欢就点个赞吧💖