隨着APP承載的業務愈來愈多,一個頁面顯示的信息也愈來愈多,須要爲不一樣的業務導流。主流的平臺APP,諸如:淘寶、京東、轉轉、盒馬、還有各種社交APP的我的主頁,都須要在頁面頂部展現核心業務數據,在底部分標籤顯示各個子業務列表數據。隨着大屏手機的普及,若是隻能經過點擊頂部標籤切換列表,對於使用場景最高的單手操做就很麻煩了。因此,爲了用戶更好的交互,須要支持子列表左右滾動切換的功能。這樣就出現了UIScrollView嵌套滾動的場景了。既須要主列表上下滾動,也須要子列表左右滾動。git
爲了解決嵌套滾動的問題,如今網上已經有許多解決方案了。包括筆者我,也開源了一個JXPagingView庫,目前已經有1100 stars,獲得許多朋友的承認。 主要支持如下特性:github
感興趣的能夠了解一下JXPagingView的原理bash
基於現有的原理,有一個小問題:當用戶在頂部header用力往上滑動的時候,當分類控制器滾動到頂部的時候,會忽然停住,列表不會接着慣性繼續滾動。你們能夠打開【京東】APP,目前(版本號:8.3.4)首頁的效果就是如此,你們能夠體驗一下,就明白我說的是什麼意思了。spa
如何才能像淘寶首頁那樣,可讓子列表接着滾動呢?直到看到了轉轉首頁,經過上手體驗以後,發現了一個全新的方案。PS:不知道轉轉APP作了什麼操做,簡單的逆向不起做用,視圖層級都看不到。因此,這個方案都是靠本身猜想加實踐折騰出來的。代理
JXPagerSmoothView Github地址,點擊立馬體驗code
能夠清楚的看到頂部pagerHeader用力往上滾動以後,下方列表會繼續滾動的,並且滾動的速度、阻尼都是系統自帶的。由於上下滾動的時候,就只是在操做一個列表,天然不會有手勢衝突之類的問題,看了下面的原理解析就明白了。自定義的pagerHeader只是用一個簡單的TableView做爲示例,你能夠用任何複雜的視圖、UICollectionView等代替。cdn
此方案原理很是簡單,沒有複雜的手勢處理,只須要處理好各類邊界狀況便可。blog
默認狀況pagerHeaderContainerView
被addSubview到當前的列表UIScrollView上面,pagerHeaderContainerView
就是頂部pagerheader(核心業務視圖區域)和pinHeader(懸浮分類控制器區域)的容器視圖。這樣子,列表上下滑動就只是在操做單個列表ScrollView,不會有滾動忽然被中斷的狀況。視圖層級以下:get
當列表在左右切換的時候、列表向上滾動到pinHeder懸浮的時候,pagerHeaderContainerView
被addSubview到JXPagerSmoothView上面,也就是脫離了列表scrollView,達到固定在頂部的效果。視圖層級以下:string
總結:就是在不斷切換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,我都會第一時間回覆你!
感謝你的閱讀,喜歡就點個贊吧💖