我們經常會用到各種分欄,比如:
第一張圖是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謝謝)
往左滑動時,藍色條的中心由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));
我來說明一下:
如上圖所示,假設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,隨意下載。
如果本文對你有用,給個??吧,謝謝~