iOS 一個滑動選擇控制元件
級別: ★★☆☆☆
標籤:「iOS」「PagesContainer」「控制元件」
作者:dac_1033
審校:QiShare團隊
我們在移動專案開發中經常會用到滑動選擇功能,例如在“米家”App的首頁中,為了展示賬戶下所有智慧裝置,或者按型別、者房間展示部分相關裝置,介面上做了如下設計:

下面我們就來介紹一下這個滑動選擇控制元件的實現過程。
1. 需求分析
上面動圖中展示的介面並不複雜,我們可以看的出來,這個滑動選擇功能主要分為兩個部分:頂部滑動選擇bar和底部可手動滑動的scrollView,並且這上下兩部分可以聯動。(底部scrollView中的子view)

需求歸納如下:
(1)頂部為滑動可選topBar;
(2)topBar可手勢左右滑動;
(3)topBar中的可選項數量可擴充套件;
(4)topBar中的選中項自動滾動至螢幕可見區域;
(5)topBar中的每一項可點選選擇,選中項樣式有變化;
(6)topBar中的選中項底部有個遊標cursor,cursor的寬度可自適應;
(7)下部為可左右滑動scrollView;
(8)scrollView根據當前topBar選中項分屏展示,即topBar與scrollView聯動;
2. 控制元件的框架層次
我們最終要將頂部topBar和下部的scrollView作為一個整體控制元件使用,因此最外層為pageContainer(繼承於UIView)將二者封裝起來,整個控制元件的初始化、設定方法都在pageContainer中。則整個控制元件的框架很簡單,如下:

3. 具體實現
本問的實現的程式碼是由部門中的前輩封裝,根據專案需求我們自己可以做一些相應的改動,首先謝謝那些前輩寫出一個這麼簡單易用的控制元件,並提供給我們學習的機會。下面我們根據控制元件的框架層次結構,從下往上依次說明實現過程。
3.1 TopBarButton
本控制元件中的TopBarButton繼承於UIButton,是控制元件頂部滑動欄中的一個item,重寫UIButton的setSelected:方法就可以設定TopBarButton在選中時的狀態。如果你的專案中對TopBarButton還有其他樣式要求,可在該類中新增其他元素,並新增相應初始化方法。
//////// .h檔案 @interface QiTopBarButton : UIButton @end //////// .m檔案 #import "QiTopBarButton.h" #define BADGE_WIDTH (13) #define TitleFontSize (13) #define ColorNormalDefault [UIColor blackColor] #define ColorSelectedDefault [UIColor redColor]; @interface QiTopBarButton() @end @implementation QiTopBarButton - (id)init { self = [super init]; if (self) { _colorNormal = ColorNormalDefault; _colorSelected = ColorSelectedDefault; [self setTitleColor:_colorSelected forState:UIControlStateSelected]; [self setTitleColor:_colorNormal forState:UIControlStateNormal]; [self.titleLabel setFont:[UIFont systemFontOfSize:TitleFontSize]]; // 設定其他子view、屬性等 } return self; } - (void)setSelected:(BOOL)selected { [super setSelected:selected]; if (selected) { [self setTitleColor:_colorSelected forState:UIControlStateNormal]; UIFont *font = [UIFont systemFontOfSize:TitleFontSize + 1 weight:UIFontWeightBold]; [self.titleLabel setFont:font]; } else { [self setTitleColor:_colorNormal forState:UIControlStateNormal]; UIFont *font = [UIFont systemFontOfSize:TitleFontSize weight:normal]; [self.titleLabel setFont:font]; } } @end 複製程式碼
3.2 PagesContainerTopBar
本控制元件中的PagesContainerTopBar繼承於UIView,其中的子view主要包括scrollView、一組topBarButton、cursor。
//////// .h檔案 #import <UIKit/UIKit.h> @protocol QiPagesContainerTopBarDelegate; @interface QiPagesContainerTopBar : UIView @property (nonatomic, weak) id<QiPagesContainerTopBarDelegate> target; // 設定相鄰button之間的間距(兩個button的title之間的距離) @property (nonatomic, assign) CGFloat buttonMargin; #pragma mark - update style - (void)setBackgroundImage:(UIImage *)image; - (void)setBackgroundImageHidden:(BOOL)isHidden; - (void)setCursorPosition:(CGFloat)percent; - (void)updateConentWithTitles:(NSArray *)titles; - (void)setIsButtonAlignmentLeft:(BOOL)isAlignmentLeft; - (void)setShowSeperateLines:(BOOL)showSeperateLines; - (void)setSelectedIndex:(NSInteger)idnex; - (NSInteger)getSelectedIndex; // 設定滑塊的顏色 - (void)setCursorColor:(UIColor *)color; // 設定滑塊長度 - (void)setCursorWidth:(CGFloat)width; // 設定滑塊長度 - (void)setCursorHeight:(CGFloat)height; // 設定按鈕選中和未選中的顏色 - (void)setTextColor:(UIColor *)normalColor andSelectedColor:(UIColor *)selectedColor; @end @protocol QiPagesContainerTopBarDelegate <NSObject> @optional // 選中topBar的一項 - (void)topBarSelectIndex:(NSInteger)index; // 重複點選topBar時會呼叫該方法 - (void)topBarSelectIndicator:(NSInteger)index; @end //////// .m檔案 #import "QiPagesContainerTopBar.h" #import "QiTopBarButton.h" //左右側的間距 #define MARGIN_HORI (0) #define CursorHeightDefault (1.5) #define BUTTON_MARGIN_DEFAULT(20) @interface QiPagesContainerTopBar () @property (nonatomic, strong) UIImageView *backgroundImageView; @property (nonatomic, strong) UIScrollView *scrollView; @property (nonatomic, strong) UIView *cursor; @property (nonatomic, assign) CGFloat cursorWidth; @property (nonatomic, assign) CGFloat cursorHeight; @property (nonatomic, strong) NSArray *arrayButtons; @property (nonatomic, assign) BOOL isButtonAlignmentLeft; @property (nonatomic, strong) NSArray *arraySeperateLines; @property (nonatomic, assign) BOOL showSeperateLines; @end @implementation QiPagesContainerTopBar - (id)init { self = [super init]; if (self) { [self setup]; } return self; } - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { [self setup]; } return self; } - (id)initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder:aDecoder]; if (self) { [self setup]; } return self; } - (void)setup { _buttonMargin = BUTTON_MARGIN_DEFAULT; _cursorHeight = CursorHeightDefault; _backgroundImageView = [[UIImageView alloc] initWithFrame:self.bounds]; [self addSubview:_backgroundImageView]; _scrollView = [[UIScrollView alloc] init]; _scrollView.showsHorizontalScrollIndicator = NO; _scrollView.showsVerticalScrollIndicator = NO; _scrollView.bounces = NO; [self addSubview:_scrollView]; _cursor = [[UIView alloc] initWithFrame:CGRectZero]; _cursor.backgroundColor = [UIColor redColor]; _cursor.layer.cornerRadius = _cursorHeight / 2.0; [_scrollView addSubview:_cursor]; } #pragma mark - 設定各個控制元件的位置 - (void)layoutSubviews { [super layoutSubviews]; CGSize size = self.frame.size; _backgroundImageView.frame = CGRectMake(0, 0, size.width, size.height);; _scrollView.frame = CGRectMake(0, 0, size.width, size.height); if ([_arrayButtons count] == 0) { return; } // 增加按鈕兩側的間距 CGFloat contentWidth = MARGIN_HORI * 2; for (int i=0; i<[_arrayButtons count]; i++) { UIButton *button = [_arrayButtons objectAtIndex:i]; contentWidth += button.frame.size.width; } // 按鈕未排滿整屏寬度時 if (!_isButtonAlignmentLeft && contentWidth < size.width) { CGFloat buttonWidth = floorf((size.width - MARGIN_HORI * 2) / [_arrayButtons count]); for (UIButton *button in _arrayButtons) { CGRect frame = button.frame; frame.size.width = buttonWidth; button.frame = frame; } } // 設定按鈕位置 NSInteger selectedIndex = 0; CGFloat xOffset = MARGIN_HORI; CGFloat buttonHeight = size.height; for (int i=0; i<[_arrayButtons count]; i++) { UIButton *button = [_arrayButtons objectAtIndex:i]; CGRect frame = button.frame; frame.origin.x = xOffset; frame.origin.y = 0; frame.size.height = buttonHeight; button.frame = frame; xOffset += frame.size.width; if (button.selected) { selectedIndex = i; } } // 設定分割線位置 for (int i=0; i<[_arraySeperateLines count]; i++) { UIView *line = [_arraySeperateLines objectAtIndex:i]; line.hidden = !_showSeperateLines; UIButton *buttonPrev = [_arrayButtons objectAtIndex:i]; UIButton *buttonNext = [_arrayButtons objectAtIndex:i+1]; CGRect frame = line.frame; frame.origin.x = (CGRectGetMaxX(buttonPrev.frame) + CGRectGetMinX(buttonNext.frame))/2; line.frame = frame; } _scrollView.contentSize = CGSizeMake(xOffset + MARGIN_HORI, size.height); // 設定遊標位置 UIButton *buttonSelected = [_arrayButtons objectAtIndex:selectedIndex]; CGRect frame = buttonSelected.frame; [buttonSelected.titleLabel sizeToFit]; CGFloat cursorWidth = _cursorWidth != 0 ? _cursorWidth : buttonSelected.titleLabel.frame.size.width; _cursor.frame = CGRectMake(frame.origin.x + (frame.size.width - cursorWidth) / 2, CGRectGetMaxY(frame) - _cursorHeight, cursorWidth, _cursorHeight); } #pragma mark - 建立各個button - (void)updateConentWithTitles:(NSArray *)titles { for (UIButton *button in _arrayButtons) { [button removeFromSuperview]; } if ([titles count] == 0) { return; } NSMutableArray *tempArray = [NSMutableArray array]; for (int i=0; i<[titles count]; i++) { NSString *title = [titles objectAtIndex:i]; UIButton *button = [self createCustomButtonWithTitle:title]; button.titleLabel.font = [UIFont systemFontOfSize:14]; button.tag = i; [button sizeToFit]; CGRect frame = button.frame; frame.size.width += _buttonMargin; button.frame = frame; [_scrollView addSubview:button]; [tempArray addObject:button]; } _arrayButtons = [NSArray arrayWithArray:tempArray]; [_scrollView bringSubviewToFront:_cursor]; [self setSelectedIndex:0]; CGFloat lineTop = CGRectGetHeight(self.frame) / 5; CGFloat lineHeight = CGRectGetHeight(self.frame) - lineTop * 2; tempArray = [NSMutableArray array]; for (int i=0; i<[_arrayButtons count]-1; i++) { UIView *line = [[UIView alloc] initWithFrame:CGRectMake(0, lineTop, 0.8, lineHeight)]; line.backgroundColor = [UIColor redColor]; [_scrollView addSubview:line]; [tempArray addObject:line]; } _arraySeperateLines = [NSArray arrayWithArray:tempArray]; } - (UIButton *)createCustomButtonWithTitle:(NSString *)title { UIButton *button = [[QiTopBarButton alloc] init]; [button setTitle:title forState:UIControlStateNormal]; [button addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside]; return button; } /** 點選topbar的某一項 */ - (void)buttonClicked:(id)sender { UIButton *button = (UIButton *)sender; NSInteger tag = button.tag; if (button.selected) { if (_target && [_target respondsToSelector:@selector(topBarSelectIndicator:)]) { [_target topBarSelectIndicator:tag]; } return; } [self setSelectedIndex:tag]; if (_target && [_target respondsToSelector:@selector(topBarSelectIndex:)]) { [_target topBarSelectIndex:tag]; } } #pragma mark 設定按鈕的位置 - (void)setIsButtonAlignmentLeft:(BOOL)isAlignmentLeft { _isButtonAlignmentLeft = isAlignmentLeft; } - (void)setShowSeperateLines:(BOOL)showSeperateLines { _showSeperateLines = showSeperateLines; } #pragma mark 更新和設定位置 - (void)setSelectedIndex:(NSInteger)index { if (index > [_arrayButtons count]) { return; } for (int i=0; i<[_arrayButtons count]; i++) { UIButton *button = [_arrayButtons objectAtIndex:i]; button.selected = (i == index); } [self updateScrollViewPosition]; } - (NSInteger)getSelectedIndex { NSInteger selectedIndex = 0; for (int i=0; i<[_arrayButtons count]; i++) { UIButton *button = [_arrayButtons objectAtIndex:i]; if (button.selected) { selectedIndex = i; } } return selectedIndex; } - (void)scrollRectToCenter:(CGRect)frame { CGSize size = self.frame.size; CGSize contentSize = self.scrollView.contentSize; CGFloat targetX = frame.origin.x - (size.width - frame.size.width) / 2; CGFloat targetEndX = targetX + size.width; if (targetX < 0) { targetX = 0; } if (targetEndX > contentSize.width) { targetEndX = contentSize.width; } CGRect targetRect = CGRectMake(targetX, 0, targetEndX - targetX, frame.size.height); [self.scrollView scrollRectToVisible:targetRect animated:YES]; } - (void)updateScrollViewPosition { CGSize size = self.frame.size; CGSize contentSize = self.scrollView.contentSize; if (size.width >= contentSize.width) { return; } CGRect frame = CGRectZero; for (int i=0; i<[_arrayButtons count]; i++) { UIButton *button = [_arrayButtons objectAtIndex:i]; if (button.selected) { frame = button.frame; } } [self scrollRectToCenter:frame]; } - (void)setCursorPosition:(CGFloat)percent { CGFloat indexOffset = percent * [_arrayButtons count]; NSInteger preIndex = floorf(indexOffset); NSInteger nextIndex = ceilf(indexOffset); if (preIndex >= 0 && nextIndex <= [_arrayButtons count]) { UIButton *preBtn = [_arrayButtons objectAtIndex:preIndex]; UIButton *nextBtn = [_arrayButtons objectAtIndex:nextIndex]; CGFloat cursorWidth = _cursorWidth; if (_cursorWidth == 0) { cursorWidth = preBtn.titleLabel.frame.size.width + (indexOffset - preIndex) * (nextBtn.titleLabel.frame.size.width - preBtn.titleLabel.frame.size.width); CGRect frame = _cursor.frame; frame.size.width = cursorWidth; _cursor.frame = frame; } CGFloat cursorCenterX = preBtn.center.x + (indexOffset - preIndex) * (nextBtn.center.x - preBtn.center.x); _cursor.center = CGPointMake(cursorCenterX , _cursor.center.y); } } - (QiTopBarButton *)getCustomButtonAtIndex:(NSInteger)index { if (index >= 0 && index < [_arrayButtons count]) { QiTopBarButton *button = [_arrayButtons objectAtIndex:index]; if ([button isKindOfClass:[QiTopBarButton class]]) { return button; } } return nil; } - (void)setBackgroundImage:(UIImage *)image { _backgroundImageView.image = image; } - (void)setBackgroundImageHidden:(BOOL)isHidden { _backgroundImageView.hidden = isHidden; } - (void)setCursorColor:(UIColor *)color { _cursor.backgroundColor = color; } - (void)setCursorWidth:(CGFloat)width { _cursorWidth = width; } - (void)setTextColor:(UIColor *)normalColor andSelectedColor:(UIColor *)selectedColor { for (QiTopBarButton *button in _arrayButtons) { button.colorNormal = normalColor; button.colorSelected = selectedColor; [button setTitleColor:normalColor forState:UIControlStateNormal]; [button setTitleColor:selectedColor forState:UIControlStateSelected]; } } @end 複製程式碼
3.3 PagesContainer
PagesContainer是最外層的View,使用者直接呼叫例項化PagesContainer並呼叫相應方法進行設定,即可使用該滑動選擇控制元件。PagesContainer將頂部topBar和下部scrollView封裝在內,topBar的點選事件與scrollView的滑動事件均傳遞到scrollViewDidScroll:方法中,再呼叫topBar的setCursorPosition:方法來設定遊標的位置。
//////// .h檔案 #import <UIKit/UIKit.h> @protocol QiPagesContainerDelegate; @class QiPagesContainerTopBar; @interface QiPagesContainer : UIView @property (nonatomic, weak) id<QiPagesContainerDelegate>delegate; /** 設定相鄰button之間的間距。間距是從該button的文字結束到下個button的文字開始之間的距離 預設值是20 */ - (void)setButtonMargin:(CGFloat)margin; /** 設定頂部的標題 */ - (void)updateContentWithTitles:(NSArray *)titles; /** 設定頂部按鈕位置 */ - (void)setIsButtonAlignmentLeft:(BOOL)isAlignmentLeft; /*! 設定是否顯示按鈕間分割線 */ - (void)setShowSeperateLines:(BOOL)showSeperateLines; /** 設定底部的View,每個View會佔據該容器的大小 */ - (void)updateContentWithViews:(NSArray *)views; /** 設定所應選擇的頁,不通知外部 */ - (void)setDefaultSelectedPageIndex:(NSInteger)index; /** 設定所應選擇的頁 */ - (void)setSelectedPageIndex:(NSInteger)index; /** 得到當前的頁面 */ - (NSInteger)getCurrentPageIndex; /** 得到當前正在顯示的view */ - (UIView *)getCurrentPageView; /** 得到index對應的view */ - (UIView *)getPageViewWithIndex:(NSInteger)index; /** 獲取頂部的tabBar */ - (QiPagesContainerTopBar*)topBar; /** 設定按鈕選中和未選中的顏色 */ - (void)setTextColor:(UIColor *)normalColor andSelectedColor:(UIColor *)selectedColor; // 設定滑塊的顏色 - (void)setCursorColor:(UIColor *)color; // 設定滑塊長度 - (void)setCursorWidth:(CGFloat)width; // 設定滑塊長度 - (void)setCursorHeight:(CGFloat)height; @end @protocol QiPagesContainerDelegate <NSObject> @optional /** page切換 */ - (void)pageContainder:(QiPagesContainer *)container selectIndex:(NSInteger)index; /** 點選當前的指示器 */ - (void)onClickPageIndicator:(QiPagesContainer *)container selectIndex:(NSInteger)index; @end //////// .m檔案 #import "QiPagesContainer.h" #import "QiPagesContainerTopBar.h" #import "QiAllowPanGestureScrollView.h" #define TOPBAR_HEIGHT34 @interface QiPagesContainer () <UIScrollViewDelegate, QiPagesContainerTopBarDelegate> @property (nonatomic, strong) QiPagesContainerTopBar *topBar; @property (nonatomic, strong) UIScrollView *scrollView; @property (nonatomic, strong) UIView *bottomLineView; @property (nonatomic, strong) NSArray *arrayViews; @property (nonatomic, assign) NSInteger currentPageIndex; @end @implementation QiPagesContainer - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { [self setup]; } return self; } - (id)init { self = [super init]; if (self) { [self setup]; } return self; } - (id)initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder:aDecoder]; if (self) { [self setup]; } return self; } - (void)setup { _topBar = [[QiPagesContainerTopBar alloc] initWithFrame:CGRectZero]; _topBar.target = self; [self addSubview:_topBar]; _scrollView = [[QiAllowPanGestureScrollView alloc] initWithFrame:CGRectZero]; _scrollView.delegate = self; _scrollView.pagingEnabled = YES; _scrollView.showsHorizontalScrollIndicator = NO; [_scrollView setScrollsToTop:NO]; [_scrollView setAlwaysBounceHorizontal:NO]; [_scrollView setAlwaysBounceVertical:NO]; [_scrollView setBounces:NO]; [self addSubview:self.scrollView]; [self layoutSubviews]; } - (void)layoutSubviews { [super layoutSubviews]; CGSize size = self.frame.size; CGFloat scrollViewHeight = size.height - TOPBAR_HEIGHT; _topBar.frame = CGRectMake(0, 0, size.width, TOPBAR_HEIGHT); _scrollView.frame = CGRectMake(0, TOPBAR_HEIGHT, size.width, scrollViewHeight); for (int i=0; i<[_arrayViews count]; i++) { UIView *v = [_arrayViews objectAtIndex:i]; v.frame = CGRectMake(i*size.width, 0, size.width, scrollViewHeight); } _scrollView.contentSize = CGSizeMake(size.width * [_arrayViews count], scrollViewHeight); } - (void)setButtonMargin:(CGFloat)margin { _topBar.buttonMargin = margin; } #pragma mark - UIScrollViewDelegate - (void)scrollViewDidScroll:(UIScrollView *)scrollView { if (scrollView.contentSize.width > 0) { [_topBar setCursorPosition:scrollView.contentOffset.x / scrollView.contentSize.width]; } } - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { NSInteger pageIndex = scrollView.contentOffset.x / scrollView.frame.size.width; if (pageIndex != _currentPageIndex) { _currentPageIndex = pageIndex; [_topBar setSelectedIndex:pageIndex]; [self notifyDelegateSelectedIndex:pageIndex]; } } #pragma mark - update content - (void)setDefaultSelectedPageIndex:(NSInteger)index { if (index >= 0 && index <= [_arrayViews count] && index != _currentPageIndex) { [_topBar setSelectedIndex:index]; _currentPageIndex = index; [_scrollView setContentOffset:CGPointMake(index * _scrollView.frame.size.width, 0) animated:YES]; } } - (void)setSelectedPageIndex:(NSInteger)index { if (index >= 0 && index <= [_arrayViews count] && index != _currentPageIndex) { [_topBar setSelectedIndex:index]; [self topBarSelectIndex:index]; } } - (NSInteger)getCurrentPageIndex { return [_topBar getSelectedIndex]; } - (UIView *)getCurrentPageView { return [_arrayViews objectAtIndex:[_topBar getSelectedIndex]]; } - (UIView *)getPageViewWithIndex:(NSInteger)index { if (index<[_arrayViews count]) { return [_arrayViews objectAtIndex:index]; } else { return nil; } } - (void)updateContentWithTitles:(NSArray *)titles { [_topBar updateConentWithTitles:titles]; } - (void)setIsButtonAlignmentLeft:(BOOL)isAlignmentLeft { [_topBar setIsButtonAlignmentLeft:isAlignmentLeft]; } - (void)setShowSeperateLines:(BOOL)showSeperateLines { [_topBar setShowSeperateLines:showSeperateLines]; } - (void)updateContentWithViews:(NSArray *)views { for (UIView *view in _arrayViews) { [view removeFromSuperview]; } if ([views count] == 0) { return; } _arrayViews = [NSArray arrayWithArray:views]; for (int i=0; i<[views count]; i++) { UIView *view = [views objectAtIndex:i]; [_scrollView addSubview:view]; } [self layoutSubviews]; } #pragma mark - QIPagesContainerTopBarDelegate - (void)topBarSelectIndex:(NSInteger)index { if (index < [_arrayViews count]) { _currentPageIndex = index; [_scrollView setContentOffset:CGPointMake(index * _scrollView.frame.size.width, 0) animated:YES]; [self notifyDelegateSelectedIndex:index]; } } /** 重複點選topBar時會呼叫該方法 */ - (void)topBarSelectIndicator:(NSInteger)index { if (index < [_arrayViews count]) { _currentPageIndex = index; [_scrollView setContentOffset:CGPointMake(index * _scrollView.frame.size.width, 0) animated:YES]; if (_delegate && [_delegate respondsToSelector:@selector(onClickPageIndicator:selectIndex:)]) { [_delegate onClickPageIndicator:self selectIndex:index]; } } } - (void)notifyDelegateSelectedIndex:(NSInteger )index { if (_delegate && [_delegate respondsToSelector:@selector(pageContainder:selectIndex:)]) { [_delegate pageContainder:self selectIndex:index]; } } #pragma mark - 設定按鈕選中和未選中的顏色 - (void)setTextColor:(UIColor *)normalColor andSelectedColor:(UIColor *)selectedColor { [_topBar setTextColor:normalColor andSelectedColor:selectedColor]; } #pragma mark - 設定滑塊的顏色 - (void)setCursorColor:(UIColor *)color { [_topBar setCursorColor:color]; } #pragma mark - 設定滑塊長度 - (void)setCursorWidth:(CGFloat)width { [_topBar setCursorWidth:width]; } #pragma mark - 設定滑塊長度 - (void)setCursorHeight:(CGFloat)height { [_topBar setCursorHeight:height]; } #pragma mark - 獲取頂部的tabBar - (QiPagesContainerTopBar*)topBar{ return _topBar; } @end 複製程式碼
3.3 控制元件在專案中的呼叫
1)實現QiPagesContainerDelegate協議,監聽控制元件的選擇事件:
#pragma mark - IHPagesContainerDelegate - (void)pageContainder:(QiPagesContainer *)container selectIndex:(NSInteger)index { } - (void)onClickPageIndicator:(QiPagesContainer *)container selectIndex:(NSInteger)index { } 複製程式碼
2)初始化控制元件,並設定控制元件樣式:
- (void) setupViews { CGFloat margin = 0; CGSize size = self.view.frame.size; NSArray *titles = [NSArray arrayWithObjects:@"我的裝置", @"臥室", @"廚房廚房廚房廚房", @"門廳", nil]; NSMutableArray *tempTableViews = [NSMutableArray array]; _pageContainer = [[QiPagesContainer alloc] initWithFrame:CGRectMake(margin, 0, size.width - margin * 2, size.height)]; _pageContainer.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; [_pageContainer setBackgroundColor:[UIColor lightGrayColor]]; _pageContainer.delegate = self; [_pageContainer updateContentWithTitles:titles]; [_pageContainer setIsButtonAlignmentLeft:YES]; [_pageContainer setCursorHeight:3.0]; [_pageContainer setCursorColor:[UIColor whiteColor]]; [_pageContainer setTextColor:[UIColor whiteColor] andSelectedColor:[UIColor whiteColor]]; UIView *topBar = (UIView *)[_pageContainer topBar]; for (int i=0; i<[titles count]; i++) { UIView *subView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, size.width, size.height - topBar.frame.size.height)]; subView.backgroundColor = [UIColor colorWithRed:arc4random()%255/255.0 green:arc4random()%255/255.0 blue:arc4random()%255/255.0 alpha:0.3]; [tempTableViews addObject:subView]; } [_pageContainer updateContentWithViews:tempTableViews]; [self.view addSubview:_pageContainer]; } 複製程式碼
自定義控制元件展示:

4. 關於cursor大小及位置設定的說明
cursor被封裝在PagesContainerTopBar內,設定cursor的位置及大小的方法是setCursorPosition:,在設定之前遊標之前,我們可以獲取到的資訊:
- setCursorPosition:位於PagesContainer中的scrollViewDidScroll:方法之中,即PagesContainer中的scrollView滾動時就會呼叫這個設定遊標的方法,傳入的引數是一個百分比scrollView.contentOffset.x / scrollView.contentSize.width;
- 頂部的PagesContainerTopBar點選選中事件、下部的scrollView滾動事件, 均會走scrollViewDidScroll:回撥方法 。
我們要滿足的需求:
- 在設定cursor的位置時,遊標的寬度大小根據位於遊標之前按鈕(preBtn)寬度和之後按鈕(nextBtn)寬度動態變化,即靠近preBtn時cursor的寬就越接近preBtn的寬,靠近nextBtn時就越接近nextBtn的寬。
實現思路:
1)獲取當前索引的偏移量(float型),並計算並找出preBtn和nextBtn:
CGFloat indexOffset = percent * [_arrayButtons count]; NSInteger preIndex = floorf(indexOffset); NSInteger nextIndex = ceilf(indexOffset); UIButton *preBtn = [_arrayButtons objectAtIndex:preIndex]; UIButton *nextBtn = [_arrayButtons objectAtIndex:nextIndex]; 複製程式碼
2)ursorWidth的最終取值是與選定按鈕的title等寬,ursorWidth的值***始終以preBtn為標準***,並根據當前滑動偏移量indexOffset-preIndex與按鈕的title長度只差的乘積進行變化:
// 如果cursor的長度是按比例變化的 ursorWidth = preBtn.titleLabel.frame.size.width + (indexOffset - preIndex) * (nextBtn.titleLabel.frame.size.width - preBtn.titleLabel.frame.size.width); 複製程式碼
3)控制元件底部的scrollView滑動時會不斷觸發scrollViewDidScroll:,便也會多次呼叫setCursorPosition:方法傳入位置,cursorWidth實時變化並更新至cursor,並設定cursor的確切位置,這樣就可以看到順暢的cursor位置及寬度變化:
CGRect frame = _cursor.frame; frame.size.width = cursorWidth; _cursor.frame = frame; CGFloat cursorCenterX = preBtn.center.x + (indexOffset - preIndex) * (nextBtn.center.x - preBtn.center.x); _cursor.center = CGPointMake(cursorCenterX , _cursor.center.y); 複製程式碼
小編微信:可加並拉入《QiShare技術交流群》。

關注我們的途徑有:
QiShare(微信公眾號)