作為簡(jiǎn)書的用戶婆翔,個(gè)人頁(yè)面肯定是常進(jìn)入的拯杠,昨天在群里聽朋友討論說,這個(gè)頁(yè)面實(shí)現(xiàn)有點(diǎn)繞啃奴,我很好奇潭陪,同時(shí)貌似我們后期也會(huì)有類似的需求,特此先學(xué)習(xí)模仿下最蕾。
第一階段
我第一個(gè)思路就是:
- 最底層是 一個(gè) UIScrollerView
- 上部是一個(gè) HeaderView 加一個(gè)自定義 選擇器
- 下部是一個(gè) UIScrollerView
- 下部的 UIScrollerView 中再套三個(gè) UITableView
但是大致做出后依溯,就立馬就遇到幾個(gè)問題:
- 1、下部 SubTableView 和 最外層的 UIScrollerView 的效應(yīng)重疊瘟则,就是在底部的時(shí)候黎炉,想滑動(dòng)里面的最底層的 ScrollerView 的時(shí)候,外面那個(gè)卻滑動(dòng)啦
- 2醋拧、還有一個(gè)就是 下部 UIScrollerView 中三個(gè) SubUITableView 滑動(dòng)起來(lái)亂走慷嗜。
- 3、傳值的不方便丹壕,如果是死的還好
- 4庆械、是否外面需要刷新呢?簡(jiǎn)書這邊是沒有刷新的
當(dāng)然最主要是解決前面兩個(gè)問題:
- 如何當(dāng)在底部的時(shí)候不滑動(dòng)下面的subTableView雀费?
方法是從scrollViewDidScroll 滑動(dòng)中判斷位置干奢,然后設(shè)置是否可以滑動(dòng)
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat maxMoveY = kHeaderViewHeight - kHeaderTitleHeight; // 最大滑動(dòng)距離
// 判斷其位置 讓其底部是否可以滑動(dòng)
if (self.backScrollView.contentOffset.y >= maxMoveY ) {
self.trendsTableView.scrollEnabled = YES;
self.articleTableView.scrollEnabled = YES;
self.moreTableView.scrollEnabled = YES;
}
else {
self.trendsTableView.scrollEnabled = NO;
self.articleTableView.scrollEnabled = NO;
self.moreTableView.scrollEnabled = NO;
}
// 當(dāng)滑到頭部時(shí),不讓其subTableView 滑動(dòng)
if (self.trendsTableView.contentOffset.y < 0) {
self.trendsTableView.scrollEnabled = NO;
}
if (self.articleTableView.contentOffset.y < 0) {
self.articleTableView.scrollEnabled = NO;
}
if (self.moreTableView.contentOffset.y < 0) {
self.moreTableView.scrollEnabled = NO;
}
}
當(dāng)然這里面還要判斷諸多咯盏袄,(判斷底部橫向ScrollerView)忿峻,此處只是針對(duì)上述那個(gè)問題而做的。
- 如何不出現(xiàn) SCrollerView 亂動(dòng)呢辕羽?
方法是 KVO 出發(fā)逛尚,通過 contentOffset 判斷,當(dāng)位置超出界限就停止滑出刁愿。
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(__unused id)object
change:(NSDictionary *)change
context:(void *)context {
CGFloat offsetX = self.bottomScrollerView.contentOffset.x;
CGFloat offsetY = self.bottomScrollerView.contentOffset.y;
CGFloat contentMaxWidth = self.bottomScrollerView.contentSize.width - SCREEN_WIDTH;
CGFloat contentMaxHeight = self.bottomScrollerView.contentSize.height - kHeaderTitleHeight - kNavBarHeight;
if (offsetX < 0 ) {
[self.bottomScrollerView setContentOffset:CGPointMake(0, offsetY)];
}
if (offsetY < 0) {
[self.bottomScrollerView setContentOffset:CGPointMake(offsetX, 0)];
}
if (offsetX >contentMaxWidth) {
[self.bottomScrollerView setContentOffset:CGPointMake(contentMaxWidth, offsetY)];
}
if (offsetY > contentMaxHeight) {
[self.bottomScrollerView setContentOffset:CGPointMake(offsetX, contentMaxHeight)];
}
}
不過實(shí)際中绰寞,只需要判斷左右不超出界限,基本就可以啦铣口,繼續(xù)嘗試...
self.bottomScrollerView.bounces = NO; // 記住設(shè)置無(wú)反彈
這個(gè)是否有反彈效果滤钱,跟具體設(shè)置有關(guān),像上面那樣設(shè)置死了脑题,為了不防止不錯(cuò)先錯(cuò)誤件缸,需要設(shè)置為NO,目前此處只是思路叔遂,后續(xù)繼續(xù)優(yōu)化中他炊。
第二階段
上述已經(jīng)實(shí)現(xiàn)的差不多啦,到目前來(lái)說已艰,目前來(lái)說最大的問題就是當(dāng)?shù)撞康膕ubTableView 滑到上面后痊末,要整體一起滑動(dòng)下來(lái),單純滑動(dòng)subTableView 不好弄哩掺,需要拖動(dòng) headerView 才可以滑動(dòng)下來(lái)凿叠。
此時(shí),用一個(gè)雞賊的方法嚼吞,當(dāng)其滑動(dòng)到那個(gè)位置 直接切換成為 最外層 ScrollerView 滑動(dòng)的狀態(tài):
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat maxMoveY = kHeaderViewHeight - kHeaderTitleHeight; // 最大滑動(dòng)距離
if (self.trendsTableView.contentOffset.y < 0) {
self.trendsTableView.scrollEnabled = NO;
[self.backScrollView setContentOffset:CGPointMake(0, maxMoveY - 1)];
}
if (self.articleTableView.contentOffset.y < 0) {
self.articleTableView.scrollEnabled = NO;
[self.backScrollView setContentOffset:CGPointMake(0, maxMoveY - 1)];
}
if (self.moreTableView.contentOffset.y < 0) {
self.moreTableView.scrollEnabled = NO;
[self.backScrollView setContentOffset:CGPointMake(0,maxMoveY - 1)];
}
}
這樣下來(lái)幔嫂,大致確實(shí)是效果實(shí)現(xiàn)啦,但是上述那個(gè)問題會(huì)有一個(gè)停頓誊薄,感覺怪怪的履恩,這里面有好多非系統(tǒng)的方法,還有諸多臨界條件的判斷呢蔫,想著是否可以改善一下啦切心,特別是頭部滑動(dòng)和整體滑動(dòng)的連貫性啦,是否最外層 View 直接可以用 UICollectionView 或 UITableView 呢片吊。
第三階段
對(duì)比他人寫的绽昏,在網(wǎng)上走了一圈,發(fā)現(xiàn)了幾個(gè)已經(jīng)寫好的俏脊,個(gè)人覺的實(shí)現(xiàn)的蠻好的全谤,而且它們都一致的采用 UICollectionView 為最底部的方法,而且封裝好啦
- HHHorizontalPagingView
- SwipeTableView
- ZJScrollPageView: 功能很多爷贫,其中有一個(gè)就是簡(jiǎn)書個(gè)人頁(yè)的這種认然,不錯(cuò)
重點(diǎn)看了HHHorizontalPagingView 的源碼补憾,它們也有類似的處理方法解決我遇到的問題,其中一點(diǎn)用 hitTest 的方法卷员,讓其做自己需要實(shí)現(xiàn)的事件盈匾,是可以在后期這樣的問題中用的:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
UIView *view = [super hitTest:point withEvent:event];
if ([view isDescendantOfView:self.headerView] || [view isDescendantOfView:self.segmentView]) {
}
return view;
}
isDescendantOfView:
方法來(lái)判定一個(gè)視圖是否在其父視圖的視圖層中,然后做相應(yīng)的處理毕骡。
用 UICollectionView 后削饵,header 和 cell 的連接自然是沒有那個(gè)卡頓的,所以最后實(shí)現(xiàn)是 將上面那個(gè) ScrollerView 換成 UICollectionView 來(lái)做未巫。
剛開始直接用UICollectionView 來(lái)寫窿撬,我感覺傳值老麻煩的,但是看完他們這邊封裝實(shí)現(xiàn)的后叙凡,發(fā)現(xiàn)他們都是將具體的 subTableView 抽離出來(lái)后劈伴,這樣更很直接傳值也碼洋麻煩。同時(shí)對(duì)比自己和他們寫的比較狭姨,發(fā)現(xiàn)自己缺少?gòu)姆庋b角度出發(fā)來(lái)實(shí)現(xiàn)宰啦,當(dāng)然是否每一個(gè)需求實(shí)現(xiàn)都需要從封裝的角度出發(fā)呢?封裝抽取思想饼拍。赡模。。
總的說來(lái)师抄,如何去實(shí)現(xiàn)漓柑,還是看自己項(xiàng)目的具體的需求,傳值是一個(gè)接口還是三個(gè)接口叨吮? 是否有刷新辆布?這個(gè)真看情況而定,如想直接用茶鉴,用上述兩個(gè)封裝好的之一锋玲,也是OK的。
備注:
https://github.com/weijingyunIOS/HHHorizontalPagingView
https://github.com/Roylee-ML/SwipeTableView