類似簡書/微博/Twitter個人中心中多分頁scollView的比較完美解決方案

背景

自以為完美的解決方案demo在此-TFMultiTabScrollView

這種界面效果需要一大段話來描述,也沒有專門的名詞,簡單說,就是簡書APP的個人簡介的樣式

簡書樣式

同樣也是微博/Twitter的個人中心樣式??偨Y(jié)下特點就是:

  • 一個頭部界面+一個分頁欄+有N個內(nèi)容分頁
  • 然后每一頁可以各自上下滑動,同時可以橫向滑動切換分頁

那么這個和網(wǎng)易新聞的界面有什么區(qū)別?看上去好像一樣。
重點就在這個頭部界面,頭部上下滾動也是可以帶動內(nèi)容上下,而頭部橫向滑動卻不會帶動內(nèi)容分頁切換

我找了下目前的一些方案,發(fā)現(xiàn)并沒有很完美的,比如嵌套UIScrollview的滑動沖突解決方案。這也是一開始我想的,似乎大家很容易走到嵌套scrollView的路線上,而這條路似乎是個死胡同。

我的方案

層級圖

界面結(jié)構(gòu)示例
view數(shù)據(jù)層次
  • 橫向滑動用來切換分頁的scrollView大小占滿整個界面,而不只是頭部以下的位置
  • N個分頁的scrollView放在橫向的ScrollView上
  • 頭部放在當前顯示的那個分頁ScrollView上

這樣做可以達到:

  • 橫向滾動可以切換分頁
  • 頭部上下滑動可以帶動當前分頁內(nèi)容上下移動,因為頭部就在內(nèi)容分頁的scrollView上

但是還有幾個問題需要解決:

  1. 分頁scrollView的內(nèi)容會被頭部遮擋一部分
  2. 橫向切換分頁后,新的分頁頭部是不是沒有了?還是需要用3個頭部?但是用3個是不是切換起來會很難看?
  3. 頭部橫向滑動時,會帶動分頁橫向切換,這個效果是不需要的

解決:

  1. 調(diào)整分頁scrollView的contentInsert.top,讓頂部內(nèi)容空出
  2. 這個問題是關鍵,當初就是想到這里而否定了,而沒有走下去。解決方法就是:
  • 在橫向滑動開始的時候,把頭部從分頁內(nèi)容scrollView(橙色的)上拿下來,放到橫向滑動的scrollView(綠色的)上,這時頭部就覆蓋在橙色scrollView上。
  • 然后根據(jù)滑動的contentOffset.x不斷調(diào)整頭部的frame,讓它看起來沒有動
  • 等到滑到目標分頁時,再把頭部又放回到當前的內(nèi)容ScrollView上。

整個過程里,視覺上看起來好像頭部視圖一直沒動。這效果就達到了。而且因為頭部視圖又放到了當前的分頁內(nèi)容ScrollView上,所以之前的效果繼續(xù)保持。

  1. 這是手勢沖突的問題,自定義頭部視圖的view,然后添加一個UIPanGestureRecognizer手勢,然后實現(xiàn)手勢沖突的方法:
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
    
    //_tabContainer是橫向滑動的scrollView
    if (otherGestureRecognizer.view == _tabContainer) {
        return NO;
    }
    
    return YES;
}

_tabContainer是橫向滑動的scrollView,如果是和頭部的pan手勢和橫向滑動ScrollView的手勢沖突了,就返回NO。因為頭部視圖在上層,所以它的首先優(yōu)先了。這樣就把橫向滑動的手勢給屏蔽了。

這么做只是為了空白的頭部視圖不會觸發(fā)橫向內(nèi)容切換,如果在頭部視圖加入更多控件,比如再加一個UIScrollView,它本身是可以自動屏蔽橫向ScrollView(綠色)的手勢的。

這樣做就避開了scrollView嵌套滑動手勢的問題

到這主要的問題就解決了,剩下的就是分頁內(nèi)容ScrollView上下滑動時,頭部的懸浮問題了。

使用KVO檢測分頁scrollView的contentOffset,然后:

  • 在向上下滑的時候,頭部跟隨上下滑,這里什么都不需要處理,因為把頭部放到了scrollView上,這是自帶效果。
  • 當tab分頁欄,就是簡書例子里“動態(tài)-文章-更多”那一欄,頂?shù)搅艘晥D的頂部后,不斷調(diào)整頭部的frame.y讓他看起來不動
  • 當頭部完全顯示出來后,也不斷調(diào)整frame.y讓它看起來不動

額外效果

  • 簡書、微博和Twitter的效果是同一種,其實還有另一種類似的,就是美團的外賣商店界面,區(qū)別就是:美團這里是內(nèi)容只要上下滑動,頭部都是跟隨動的,而簡書微博這邊是只有內(nèi)容視圖滑到頂部,也就是內(nèi)容的頂接住了頭部視圖的頂?shù)臅r候,才能拖動頭部。在邏輯上,美團那種跟Safari的地址欄是一個感覺。
    為了區(qū)分這兩種效果,設置了一個屬性moveHeaderOnlyContentTop,YES時就是簡書微博效果,NO就是美團外賣商店效果。默認YES。

  • 有些分頁內(nèi)容可能很少,導致tab分頁欄滑不到頂部,而如果其他分頁可以滑到頂部,這時就有一個問題,tab欄在頂部的時候,切換到內(nèi)容不足的頁面,就會導致tab分頁欄刷的一下掉下來。這效果很不好。所以我加了一個屬性autoFillContent。如果YES,則計算內(nèi)容高度,設置contentInsert.bottom,在scrollView的底部增加一段空白。這樣內(nèi)容不足時,tab分頁欄依然可以到頂部。默認YES。

  • 有時tab分頁欄不想直接貼住視圖的頂部,比如有一個導航欄效果,內(nèi)容往上滑的時候,導航欄出來,往下滑時候呢,導航欄漸變消失。這時要給導航欄留出空位,tab分頁欄就不能直接挨著頂部,所以我加了一個屬性topSpace,這個是用來調(diào)控tab欄和頂部的間距的。默認0。

不完美的嘗試

1. 三層嵌套scrollView

這種界面結(jié)構(gòu),很容易想到的就是3個scrollView嵌套:一個大的豎向scrollView----橫向分頁scrollView----每個分頁自身的scrollView。最外層的豎向scrollView為了是頭部能夠滑動,所以說這個頭部才是這里的癥結(jié)??!但這種結(jié)構(gòu)帶來的是兩個豎向的scrollView的滑動切換問題:

  • 當tab分頁欄滑到定后,要滑動內(nèi)部的scrollView
  • 到頭部除了tab欄還有更多顯示出來時,要滑動外部的scrollView來帶動頭部。

那么為什么不一直滑動外面的scrollView呢?這其實也是一種方案,只要把內(nèi)容視圖的內(nèi)容完全的展開,然后只滑動外層的scrollView來切換顯示內(nèi)容。不好的是這樣就沒法使用tableView的重用功能了,比如你有很多的cell,像簡書的動態(tài)那一欄。但分頁是可以做的,只要加載新的內(nèi)容后在繼續(xù)全部展開。但這個不完美的瑕疵讓人不爽。

一開始我以為scrollView的哪個滑動是hitView的問題,但后來發(fā)現(xiàn)scrollView的滑動是它自帶的pan手勢來控制滑動的。手勢一旦識別了,就不會再識別,除非是新的觸碰,這就是這種方案為什么要放手再重新滑動才能內(nèi)外scrollView切換。

如果scrollView的滑動不是它自帶的手勢處理,而是一個統(tǒng)一的手勢,而scrollView只要接受來自這個統(tǒng)一的手勢的滑動數(shù)據(jù)就好了。這樣只需要把數(shù)據(jù)的輸出調(diào)整成另一個scrollView就可以完成完美的滑動切換。這里就牽扯到一點設計的問題了。

2. 模擬滾動的頭部視圖

這種方案是:
整體的父視圖 ---- 橫向的scrollView ---- 分頁的豎向scrollView
整體的父視圖 ----- 頭部

也就是頭部視圖和橫向的scrollView是同一個層級上,然后頭部在上面覆蓋。

這樣要要解決的關鍵問題就是:頭部上豎向滑動,要帶動分頁內(nèi)容色scrollView滑動。而橫向滑動啊,點擊啊之類的都沒有問題了,因為它已經(jīng)不再嵌套scrollView的層級里了。

所以我自定義了一個頭部(demo里的TFScrollSimulateView),然后給它加了pan手勢來模擬scrollView的滾動,其實手指拖動是很好實現(xiàn)的,頭部的手勢拖動了多少就修改scrollView的contentOffset多少,麻煩的是:

  • 手指快速的一滑,手指離開后,scrollView還要繼續(xù)滾動,而且要速度越來越慢。
  • scrollView拉倒邊緣時的彈簧效果

所以我做了一個計時器,在手指離開后,不斷的計算滑動的距離,而pan手勢是有velocityInView:方法可以取得速度。這樣其實基本可以解決問題了,但有個不完美的是scrollView減速的公式只能猜:
module -= KTimerInterval * _friction * (10000 + module * module); //阻力和速度平方成正比,速度減去a*t

但是后來在一個項目里找到了相關公式:彈簧公式

彈簧公式
3. 用圖片欺騙

其實如果不需要橫向滑動切換分頁的效果,只能點擊切換的話,這個問題的難度直線下降,因為這樣就不需要橫向的scrollView了,內(nèi)外層的scrollView可以合成一個,就完全不存在什么嵌套的問題了。

但是如果一定要有橫向切換的效果呢?可以在開始橫向滑動的時候,做兩張圖片覆蓋在內(nèi)容部分。手指滑動的時候,是圖片在切換,但是這個圖片長得跟分頁的scrollView一樣,讓人看起來好像是分頁內(nèi)容切換了一樣。

  • 使用core graphic的截圖方法,在剛開始滑動的時候,把當前分頁scrollView的界面截取下來,然后覆蓋到相同位置。(就跟你把別人的桌面截一張圖,然后做成背景,再把桌面圖標全隱藏,看起來好像沒變-_-)
  • 把之前的內(nèi)容圖片擺在左右,然后滑動切換時,實際是這兩個圖片在切換。

這個具體沒去實現(xiàn),只是想法,這種欺騙眼睛的手法是挺有意思的。

2017.6.28更新


修改了問題:

  1. 分頁scrollView在滑動過程中會添加新的子視圖,導致遮住頭部,比如UITableViewsectionHeader.
  2. _currentVisableHeaderH沒有初始值導致滑動出錯
  3. 分頁切換時,如果頻繁點擊會導致界面閃爍

完善功能:

  1. 在不需要自動擴充底部內(nèi)容的時候,有可能某個分頁內(nèi)容很少,導致頭部滑動不到頂部,在切換的時候會出現(xiàn)“突然掉下來”的現(xiàn)象,現(xiàn)在改成動畫,體驗更好些。
  2. 做好了某個分頁滑動,其他分頁同步滑動內(nèi)容的處理。能夠達到從A分頁離開時,如果你已經(jīng)滑動到了第10行是貼著頭部視圖的底部的,在其他分頁移動了頭部視圖后,A分頁的內(nèi)容也會跟隨移動,保證回去時還是第10行貼著頭部。但是這個只有在moveHeaderOnlyContentTopNO的時候有效。
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 1、通過CocoaPods安裝項目名稱項目信息 AFNetworking網(wǎng)絡請求組件 FMDB本地數(shù)據(jù)庫組件 SD...
    陽明AI閱讀 16,170評論 3 119
  • 出來混,遲早都是要還的。原諒我沒有在第一時間記錄那些美好的瞬間。無意間翻看到手機里的照片,讓我們一起回憶… ...
    范琳琳123閱讀 758評論 0 0
  • 5月24號我終于跟上了隊伍,早上七點到達麗江,初到麗江我就喜歡這里,可能是太早,麗江很安靜,溫暖的陽光傾瀉在...
    莎莎西閱讀 488評論 2 2
  • 傷,有身體的傷,還有心里的傷。有傷就有痛,不管是心里的還是身體上都會有痛感,區(qū)別在于一個痛在心里,一個痛在神...
    吳楓WF閱讀 281評論 0 0
  • 盼望已久的寒假終于來了,因為我終于可以自己做個“小大人”了??梢宰约汗芾碜约毫?,合理安排生活,我的安排是這樣...
    楊京瑜閱讀 266評論 1 1

友情鏈接更多精彩內(nèi)容