前言
隨著APP承載的業(yè)務(wù)越來越多,一個(gè)頁面顯示的信息也越來越多盾致,需要為不同的業(yè)務(wù)導(dǎo)流。主流的平臺APP庭惜,諸如:淘寶罩驻、京東惠遏、轉(zhuǎn)轉(zhuǎn)、盒馬百揭、還有各類社交APP的個(gè)人主頁熏版,都需要在頁面頂部展示核心業(yè)務(wù)數(shù)據(jù),在底部分標(biāo)簽顯示各個(gè)子業(yè)務(wù)列表數(shù)據(jù)柿顶。隨著大屏手機(jī)的普及渺贤,如果只能通過點(diǎn)擊頂部標(biāo)簽切換列表,對于使用場景最高的單手操作就很麻煩了志鞍。所以,為了用戶更好的交互固棚,需要支持子列表左右滾動切換的功能统翩。這樣就出現(xiàn)了UIScrollView嵌套滾動的場景了此洲。既需要主列表上下滾動,也需要子列表左右滾動呜师。
現(xiàn)有的解決方案
為了解決嵌套滾動的問題,現(xiàn)在網(wǎng)上已經(jīng)有許多解決方案了汁汗。包括筆者我衷畦,也開源了一個(gè)JXPagingView庫祈争,目前已經(jīng)有1100 stars,得到許多朋友的認(rèn)可角寸。
主要支持以下特性:
- 列表懶加載
- 主列表、子列表下拉刷新
- 懸浮位置調(diào)整
- OC與Swift雙版本
- 封裝度高袭厂,使用方便
感興趣的可以了解一下JXPagingView的原理
基于現(xiàn)有的原理球匕,有一個(gè)小問題:當(dāng)用戶在頂部header用力往上滑動的時(shí)候,當(dāng)分類控制器滾動到頂部的時(shí)候亮曹,會突然停住,列表不會接著慣性繼續(xù)滾動照卦。大家可以打開【京東】APP,目前(版本號:8.3.4)首頁的效果就是如此役耕,大家可以體驗(yàn)一下,就明白我說的是什么意思了瞬痘。
如何才能像淘寶首頁那樣,可以讓子列表接著滾動呢框全?直到看到了轉(zhuǎn)轉(zhuǎn)首頁察绷,通過上手體驗(yàn)之后拆撼,發(fā)現(xiàn)了一個(gè)全新的方案容劳。PS:不知道轉(zhuǎn)轉(zhuǎn)APP做了什么操作闸度,簡單的逆向不起作用,視圖層級都看不到筋岛。所以,這個(gè)方案都是靠自己猜測加實(shí)踐折騰出來的睁宰。
JXPagerSmoothView方案
JXPagerSmoothView Github地址,點(diǎn)擊立馬體驗(yàn)
效果預(yù)覽
可以清楚的看到頂部pagerHeader用力往上滾動之后柒傻,下方列表會繼續(xù)滾動的,而且滾動的速度红符、阻尼都是系統(tǒng)自帶的。因?yàn)樯舷聺L動的時(shí)候预侯,就只是在操作一個(gè)列表,自然不會有手勢沖突之類的問題萎馅,看了下面的原理解析就明白了双戳。自定義的pagerHeader只是用一個(gè)簡單的TableView作為示例糜芳,你可以用任何復(fù)雜的視圖、UICollectionView等代替峭竣。
此方案原理非常簡單,沒有復(fù)雜的手勢處理皆撩,只需要處理好各種邊界情況即可。
情況一
默認(rèn)情況pagerHeaderContainerView
被addSubview到當(dāng)前的列表UIScrollView上面毅访,pagerHeaderContainerView
就是頂部pagerheader(核心業(yè)務(wù)視圖區(qū)域)和pinHeader(懸浮分類控制器區(qū)域)的容器視圖。這樣子喻粹,列表上下滑動就只是在操作單個(gè)列表ScrollView,不會有滾動突然被中斷的情況守呜。視圖層級如下:
情況二
當(dāng)列表在左右切換的時(shí)候山憨、列表向上滾動到pinHeder懸浮的時(shí)候,pagerHeaderContainerView
被addSubview到JXPagerSmoothView上面郁竟,也就是脫離了列表scrollView,達(dá)到固定在頂部的效果棚亩。視圖層級如下:
總結(jié):就是在不斷切換pagerHeaderContainerView
的父視圖,達(dá)到淘寶讥蟆、轉(zhuǎn)轉(zhuǎn)首頁的效果。是不是原理很簡單瘸彤?當(dāng)然使用的代碼也很簡單!
使用示例
1笛钝、初始化JXPagerSmoothView
self.pager = [[JXPagerSmoothView alloc] initWithDataSource:self];
[self.view addSubview:self.pager];
2、初始化pagerHeader
和pinHeader
self.categoryView = [[JXCategoryTitleView alloc] init];
self.categoryView.titles = @[@"能力", @"愛好", @"隊(duì)友"];
self.categoryView.contentScrollViewClickTransitionAnimationEnabled = NO;
self.pagerHeader = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"lufei.jpg"]];
3玻靡、實(shí)現(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、列表實(shí)現(xiàn)JXPagerSmoothViewListViewDelegate
代理方法
SmoothListViewController
類實(shí)現(xiàn)JXPagerSmoothViewListViewDelegate
代理方法
- (UIScrollView *)listScrollView {
return self.tableView;
}
- (UIView *)listView {
return self.view;
}
使用注意事項(xiàng)
通過示例代碼可以看到整個(gè)邏輯簡單潭陪、清晰,和使用UITableView
一樣,只需要實(shí)現(xiàn)對應(yīng)的代理方法即可老厌,根本不需要操心頁面的交互邏輯瘟则。真正的做到了高內(nèi)聚低耦合醋拧、職責(zé)分離等原則。
但是有幾個(gè)點(diǎn)需要注意:
- 不要自己設(shè)置列表滾動視圖的
contentInset
屬性淀弹,內(nèi)部通過設(shè)置contentInset
來添加pagerHeaderContainerView
; - 當(dāng)頂部pagerHeader是一個(gè)UIScrollView及其子類時(shí),需要讓contentSize.height=pagerHeader的高度薇溃,即不能讓其能夠滾動,詳情可以參考OC示例demo的
SmoothCustomPagerHeaderViewController
類沐序; - 請仔細(xì)辨別
JXPagerView
和JXPagerSmoothView
的區(qū)別堕绩,并選擇適合自己需求的類; -
JXPagerSmoothView
在1.2.1及以上版本才有奴紧,請使用最新版本; - Swift版本是
JXPagingSmoothView
;
JXPagerSmoothView
Github地址
JXPagerSmoothView Github地址晶丘,點(diǎn)擊立馬體驗(yàn)
總結(jié)
JXPagerSmoothView
的實(shí)現(xiàn)文件只有300行代碼左右,需要深入研究的朋友浅浮,相信花點(diǎn)功夫就能看懂。這樣子以后業(yè)務(wù)上面有任何特殊要求時(shí)脑题,都可以自己實(shí)現(xiàn)。只要掌握了原理叔遂,就不怕需求的變化。
有任何建議或疑問已艰,可以留言痊末、提Issues哩掺,我都會第一時(shí)間回復(fù)你!
感謝你的閱讀嚼吞,喜歡就點(diǎn)個(gè)贊吧??