前言
隨著APP承載的業(yè)務越來越多,一個頁面顯示的信息也越來越多,需要為不同的業(yè)務導流。主流的平臺APP,諸如:淘寶、京東、轉轉、盒馬、還有各類社交APP的個人主頁,都需要在頁面頂部展示核心業(yè)務數(shù)據(jù),在底部分標簽顯示各個子業(yè)務列表數(shù)據(jù)。隨著大屏手機的普及,如果只能通過點擊頂部標簽切換列表,對于使用場景最高的單手操作就很麻煩了。所以,為了用戶更好的交互,需要支持子列表左右滾動切換的功能。這樣就出現(xiàn)了UIScrollView嵌套滾動的場景了。既需要主列表上下滾動,也需要子列表左右滾動。
現(xiàn)有的解決方案
為了解決嵌套滾動的問題,現(xiàn)在網(wǎng)上已經(jīng)有許多解決方案了。包括筆者我,也開源了一個JXPagingView庫,目前已經(jīng)有1100 stars,得到許多朋友的認可。
主要支持以下特性:
- 列表懶加載
- 主列表、子列表下拉刷新
- 懸浮位置調(diào)整
- OC與Swift雙版本
- 封裝度高,使用方便
感興趣的可以了解一下JXPagingView的原理
基于現(xiàn)有的原理,有一個小問題:當用戶在頂部header用力往上滑動的時候,當分類控制器滾動到頂部的時候,會突然停住,列表不會接著慣性繼續(xù)滾動。大家可以打開【京東】APP,目前(版本號:8.3.4)首頁的效果就是如此,大家可以體驗一下,就明白我說的是什么意思了。
如何才能像淘寶首頁那樣,可以讓子列表接著滾動呢?直到看到了轉轉首頁,通過上手體驗之后,發(fā)現(xiàn)了一個全新的方案。PS:不知道轉轉APP做了什么操作,簡單的逆向不起作用,視圖層級都看不到。所以,這個方案都是靠自己猜測加實踐折騰出來的。
JXPagerSmoothView方案
JXPagerSmoothView Github地址,點擊立馬體驗
效果預覽
可以清楚的看到頂部pagerHeader用力往上滾動之后,下方列表會繼續(xù)滾動的,而且滾動的速度、阻尼都是系統(tǒng)自帶的。因為上下滾動的時候,就只是在操作一個列表,自然不會有手勢沖突之類的問題,看了下面的原理解析就明白了。自定義的pagerHeader只是用一個簡單的TableView作為示例,你可以用任何復雜的視圖、UICollectionView等代替。
此方案原理非常簡單,沒有復雜的手勢處理,只需要處理好各種邊界情況即可。
情況一
默認情況pagerHeaderContainerView
被addSubview到當前的列表UIScrollView上面,pagerHeaderContainerView
就是頂部pagerheader(核心業(yè)務視圖區(qū)域)和pinHeader(懸浮分類控制器區(qū)域)的容器視圖。這樣子,列表上下滑動就只是在操作單個列表ScrollView,不會有滾動突然被中斷的情況。視圖層級如下:
情況二
當列表在左右切換的時候、列表向上滾動到pinHeder懸浮的時候,pagerHeaderContainerView
被addSubview到JXPagerSmoothView上面,也就是脫離了列表scrollView,達到固定在頂部的效果。視圖層級如下:
總結:就是在不斷切換pagerHeaderContainerView
的父視圖,達到淘寶、轉轉首頁的效果。是不是原理很簡單?當然使用的代碼也很簡單!
使用示例
1、初始化JXPagerSmoothView
self.pager = [[JXPagerSmoothView alloc] initWithDataSource:self];
[self.view addSubview:self.pager];
2、初始化pagerHeader
和pinHeader
self.categoryView = [[JXCategoryTitleView alloc] init];
self.categoryView.titles = @[@"能力", @"愛好", @"隊友"];
self.categoryView.contentScrollViewClickTransitionAnimationEnabled = NO;
self.pagerHeader = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"lufei.jpg"]];
3、實現(xiàn)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;
}
4、列表實現(xiàn)JXPagerSmoothViewListViewDelegate
代理方法
SmoothListViewController
類實現(xiàn)JXPagerSmoothViewListViewDelegate
代理方法
- (UIScrollView *)listScrollView {
return self.tableView;
}
- (UIView *)listView {
return self.view;
}
使用注意事項
通過示例代碼可以看到整個邏輯簡單、清晰,和使用UITableView
一樣,只需要實現(xiàn)對應的代理方法即可,根本不需要操心頁面的交互邏輯。真正的做到了高內(nèi)聚低耦合、職責分離等原則。
但是有幾個點需要注意:
- 不要自己設置列表滾動視圖的
contentInset
屬性,內(nèi)部通過設置contentInset
來添加pagerHeaderContainerView
; - 當頂部pagerHeader是一個UIScrollView及其子類時,需要讓contentSize.height=pagerHeader的高度,即不能讓其能夠滾動,詳情可以參考OC示例demo的
SmoothCustomPagerHeaderViewController
類; - 請仔細辨別
JXPagerView
和JXPagerSmoothView
的區(qū)別,并選擇適合自己需求的類; -
JXPagerSmoothView
在1.2.1及以上版本才有,請使用最新版本; - Swift版本是
JXPagingSmoothView
;
JXPagerSmoothView
Github地址
JXPagerSmoothView Github地址,點擊立馬體驗
總結
JXPagerSmoothView
的實現(xiàn)文件只有300行代碼左右,需要深入研究的朋友,相信花點功夫就能看懂。這樣子以后業(yè)務上面有任何特殊要求時,都可以自己實現(xiàn)。只要掌握了原理,就不怕需求的變化。
有任何建議或疑問,可以留言、提Issues,我都會第一時間回復你!
感謝你的閱讀,喜歡就點個贊吧??