知乎分欄滑動效果實現--附Demo

我們經常會用到各種分欄,比如:

app store分欄
知乎分欄

第一張圖是app store排行榜的分欄,它最普通的分欄,使用系統的UISegmentControl即可實現(是不是發現這個導航條很高,或者說UISegmentControl與導航條看起來是一個整體,我之前研究了一下,實現方法在這里:App Store 排行榜導航條實現)。

第二張圖是知乎"通知"模塊導航實現,用過知乎的小伙伴,如果細心的話會發現當左右滾動通知模塊時,頂部的分段控件會發生如下變化:

  • 分段控件的標題顏色變化,選中則為黑色,未選中則為灰色
  • 標題底部有一根藍色的條,選中那個標題,藍色的條就會在哪個標題下面
  • 藍色的條在輕輕滑動頁面時,藍色的條也會跟隨滑動,且帶有過度效果
  • 藍色的條在跟隨滑動時,長度也會隨著文本的變化而變化,且帶有過度效果

我們平時的實現,或者一些開源庫的實現一般只是做到了前面兩點,雖然功能都實現了,但是用起來卻沒有絲滑般的感受啊??。所以強迫癥的我就研究了一下如何實現后兩點中提到的效果。

這里為了方便,我使用了一個分段控件的開源庫:RKSwipeBetweenViewControllers。它很好的實現了前面兩點功能,而且想要實現后兩點的話直接修改它的代碼也是非常方便的。

我們先看一下第三點的效果如何實現,我們需要在頁面滾動時,藍色的條跟隨滾動,所以需要不停的變化藍色條的坐標,因此關鍵代碼在UIScollView的代理scrollViewDidScroll:中。
實現代碼如下:

-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    CGFloat percentX = (scrollView.contentOffset.x - CGRectGetWidth(scrollView.frame)) / CGRectGetWidth(scrollView.frame);//往左滑動大于0,往右小于0(由于默認偏移320)
    NSLog(@"percentX:%f",percentX);
    
    NSInteger currentPageIndex = self.currentPageIndex;
    
    UIButton *curBtn = [[navigationView subviews] objectAtIndex:currentPageIndex];
    CGFloat centerxOfSelectionBar = curBtn.center.x + percentX*_titleWidth;
    selectionBar.center = CGPointMake(centerxOfSelectionBar, selectionBar.center.y);
}

首先說明一下,這里scrollview的contentOffset.x默認偏移了320(即屏幕寬,我用的iPhone SE,屏寬320)。切每切換一個頁面,偏移都會被修改為320。
這里percentX是一個偏移度,絕對值小于或等于1。往左滑動大于0,往右小于0。
這里我是通過藍色條的中心點來確定藍色條的位置的,centerxOfSelectionBar是計算的藍色條的中心。而_titleWidth是頂部標題按鈕的寬度(這里標題是用的按鈕,等寬)。

CGFloat centerxOfSelectionBar = curBtn.center.x + percentX*_titleWidth;

這行應該比較好理解,看下面這張圖(不要夸我,我知道我畫的好O(∩_∩)O謝謝)

0.jpeg

往左滑動時,藍色條的中心由1滑動到2,1到2的間距正好是_titleWidth,所以1到2任意一點的x坐標就是1的x坐標加上偏移的量,即上面代碼所示。

這樣就實現了第三點的效果。

至于第四點中提到的效果,很明顯也是在同一個代理中實現,實現如下:

    NSInteger currentPageIndex = self.currentPageIndex;

    UIButton *curBtn = [[navigationView subviews] objectAtIndex:currentPageIndex];

    NSInteger targetPage = percentX > 0 ? currentPageIndex+1:currentPageIndex-1;
    if (targetPage >= 0 && targetPage < _buttonText.count)
    {
        UIButton *targetBtn = [[navigationView subviews] objectAtIndex:targetPage];
        
        CGSize sizeOrigin = MB_TEXTSIZE(curBtn.currentTitle, [UIFont systemFontOfSize:FONT_SIZE_16]);
        CGFloat lengthOrigin = sizeOrigin.width;
        
        CGSize sizeTarget = MB_TEXTSIZE(targetBtn.currentTitle, [UIFont systemFontOfSize:FONT_SIZE_16]);
        CGFloat lengthTarget = sizeTarget.width;
        
        CGFloat selctionBarLength = lengthTarget + (lengthOrigin - lengthTarget)*(1-ABS(percentX));
        selectionBar.frame = CGRectMake(selectionBar.frame.origin.x, selectionBar.frame.origin.y, selctionBarLength, selectionBar.frame.size.height);
    }

MB_TEXTSIZE是MBProgress中定義的計算文字尺寸的宏定義,我直接拷過來用的。
上面的代碼我想大部分應該都容易看明白,就只有下面這行有點麻煩:

CGFloat selctionBarLength = lengthTarget + (lengthOrigin - lengthTarget)*(1-ABS(percentX));

我來說明一下:


FullSizeRender.jpg

如上圖所示,假設a位置的標題長度為a,b位置長度為b,由a滑動到b,L是目標長度,即最后停留位置藍色條的長度:

  • 如果a>b,L1 = b + (a - b)*(1-ABS(percentX))
  • 如果b>a,L2 = b - (b - a)*(1-ABS(percentX))
    對比一下會發現,L1 與 L2 是相等的,所以公式可以簡化為
L = b - (b - a)*(1-ABS(percentX))

demo地址在這:SegmentControlDemo,隨意下載。

如果本文對你有用,給個??吧,謝謝~

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 問答題47 /72 常見瀏覽器兼容性問題與解決方案? 參考答案 (1)瀏覽器兼容問題一:不同瀏覽器的標簽默認的外補...
    _Yfling閱讀 13,801評論 1 92
  • ¥開啟¥ 【iAPP實現進入界面執行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個線程,因...
    小菜c閱讀 6,523評論 0 17
  • 公元2010年9月8日,我和堂弟乘車來到南昌,即將在這座不尷不尬的城市開始我們新的學習生涯。正值酷暑,南昌熱得過分...
    二文閱讀 366評論 0 1
  • 限定性與非限定性從句 限定性從句限制了主語的特征,以下面句子為例: The suspect in the line...
    Nuk閱讀 410評論 0 0
  • 從小我們就可以相互感知,最近已經連續幾天都做同樣的夢了,是你回來了對么?
    小水1021閱讀 128評論 3 0