級別: ★★☆☆☆
標簽:「iOS」「分頁」「QiPageMenuView」
作者: 沐靈洛
審校: QiShare團隊
iOS 快速實現分頁界面的搭建
項目中我們經常會遇到滾動分頁的設計效果,被用來對不同數據界面的展示進行分類。我們先可以來預覽一下實現效果:
實現分析
根據動圖進行實現分析:這個效果的實現分為兩部分頂部的QiPageMenuView和內容展示部分QiPageContentView:
QiPageMenuView是基于UIScrollView實現的,我們可以按照自己的項目需求,定制自己需要實現的效果。QiPageMenuView提供了可設置的屬性有:菜單每一項是否根據文字的大小自適應寬度還是設置固定寬度、菜單的首項和最后一項距離父視圖的間距、每一項之間的間距、包括了每一項QiPageItem的展示效果自定義等。也實現了菜單項超出一屏幕時自動滑動顯示的效果:
QiPageContentView是基于UIPageViewController實現的封裝,在項目中可能有多處這樣的界面效果。單純的使用UIPageViewController寫在相應的控制器中,添加相應的子控制器,通過實現UIPageViewController的數據源和代理協議也可以達到這種效果。但是UIPageViewController嵌套在主控制器中,耦合度比較高,代碼量也比較大,若是多處需要使用這種效果,每次都寫一遍UIPageViewController,效率和可移植性不高。 QiPageMenuView和QiPageContentView之間是解耦合的,彼此都可以作為單獨的控件去使用。在設計構思時,菜單視圖的樣式有可能是QiPageMenuView樣式之外的視圖,若是兩者耦合度太高,就變成了QiPageContentView + QiPageMenuView組合,能展現的效果就被限制了。
QiPageMenuView實現與使用
- QiPageMenuView.h中展現了QiPageMenuView可實現定制的相關屬性以及初始化方法介紹。
@interface QiPageMenuView : UIScrollView<QiPageControllerDelegate>
/**菜單欄點擊事件*/
@property (nonatomic,copy)void(^pageItemClicked)(NSInteger clickedIndex,QiPageMenuView *menu);
/**常態item的字體顏色*/
@property (nonatomic,strong)UIColor *normalTitleColor;
/**選中item的字體顏色*/
@property (nonatomic,strong)UIColor *selectedTitleColor;
/**常態item的字體*/
@property (nonatomic,strong)UIFont *titleFont;
/**選中Item的字體*/
@property (nonatomic,strong)UIFont *selectedTitleFont;
/**字體距離item兩邊的間距,itemsAutoResizing = YES時 設置有效*/
@property (nonatomic,assign)CGFloat itemTitlePadding;
/**item距上的間距。itemIsVerticalCentred = NO的時候設置有效*/
@property (nonatomic,assign)CGFloat itemTopPadding;
/**items的左邊縮進*/
@property (nonatomic,assign)CGFloat leftMargin;
/**items的右邊縮進*/
@property (nonatomic,assign)CGFloat rightMargin;
/**是否根據文字的長度自動計算item的width default YES*/
@property (nonatomic,assign)BOOL itemsAutoResizing;
/**item是否垂直居中顯示,默認yes; itemTopPadding 與 lineTopPadding 不會生效;設置NO itemHeight會自適應高*/
@property (nonatomic,assign)BOOL itemIsVerticalCentred;
/**item之間的間距*/
@property (nonatomic,assign)CGFloat itemSpace;
/**每個item的高度*/
@property (nonatomic,assign)CGFloat itemHeight;
/**每個item的寬度。itemsAutoResizing = YES不必賦值也可。反之必須給值。*/
@property (nonatomic,assign)CGFloat itemWidth;
/**是否顯示下劃線 default YES*/
@property (nonatomic,assign)BOOL hasUnderLine;
/**下劃線顏色*/
@property (nonatomic,strong)UIColor *lineColor;
/**下劃線到item的間距*/
@property (nonatomic,assign)CGFloat lineTopPadding;
/**下劃線的高度*/
@property (nonatomic,assign)CGFloat lineHeight;
/**下劃線的寬度*/
@property (nonatomic,assign)CGFloat lineWitdh;
/**pageController滑動完成*/
@property (nonatomic,assign)NSInteger pageScrolledIndex;
/**初始化方法*/
- (instancetype)initWithFrame:(CGRect)frame titles:(NSArray*)titles dataSource:(NSDictionary<QiPageMenuViewDataSourceKey, id> *)dataSource;
- (instancetype)initWithFrame:(CGRect)frame titles:(NSArray*)titles;
/**滑動到某一項 @param pageItem item*/
- (void)scrollToPageItem:(QiPageItem*)pageItem;
/*!@brief 更新標題數組@param items selectedIndex重置選中的item*/
- (void)updateMenuViewWithNewItemArray:(NSArray *)items selectedIndex:(NSInteger)selectedIndex;
@end復制代碼
- QiPageMenuView滑動顯示的核心代碼
- (void)scrollToPageItem:(QiPageItem*)pageItem {[self refreshUnderLineViewPosition:pageItem];if (self.contentSize.width <= self.width) {return;}CGRect originalRect = pageItem.frame;CGRect convertRect = [self convertRect:originalRect toView:self.superview];CGFloat targetX;CGFloat realMidX = CGRectGetMinX(originalRect)+CGRectGetWidth(originalRect)/2;if (CGRectGetMidX(convertRect) < CGRectGetMidX(self.frame)) {//是否需要右滑if (realMidX> CGRectGetMidX(self.frame)) {targetX = realMidX-CGRectGetMidX(self.frame);}else {targetX = 0;}[self setContentOffset:CGPointMake(targetX, 0) animated:YES];} else if (CGRectGetMidX(convertRect) > CGRectGetMidX(self.frame)) {if (realMidX+CGRectGetMidX(self.frame)<self.contentSize.width) {targetX = realMidX-CGRectGetMidX(self.frame);} else {targetX = self.contentSize.width - CGRectGetMaxX(self.frame);}[self setContentOffset:CGPointMake(targetX, 0) animated:YES];}
}
復制代碼
- QiPageMenuView使用的兩種方式
- 方式一:
//定制樣式NSDictionary *dataSource = @{QiPageMenuViewNormalTitleColor : [UIColor blackColor],QiPageMenuViewSelectedTitleColor : [UIColor redColor],QiPageMenuViewTitleFont : [UIFont systemFontOfSize:14],QiPageMenuViewSelectedTitleFont : [UIFont systemFontOfSize:14],QiPageMenuViewItemIsVerticalCentred : @(YES),QiPageMenuViewItemTitlePadding : @(10.0),QiPageMenuViewItemTopPadding : @(20.0),QiPageMenuViewItemPadding : @(10.0),QiPageMenuViewLeftMargin : @(20.0),QiPageMenuViewRightMargin : @(20.0),QiPageMenuViewItemsAutoResizing : @(YES),QiPageMenuViewItemWidth : @(90.0),QiPageMenuViewItemHeight : @(40.0),QiPageMenuViewHasUnderLine :@(YES),QiPageMenuViewLineColor : [UIColor greenColor],QiPageMenuViewLineWidth : @(30.0),QiPageMenuViewLineHeight : @(4.0),QiPageMenuViewLineTopPadding : @(10.0)};QiPageMenuView *menuView = [[QiPageMenuView alloc]initWithFrame:CGRectMake(0, 0, self.view.width, 50) titles:@[@"消息",@"節日消息",@"廣播通知",@"QISHARE",@"奇舞團"] dataSource:dataSource];menuView.backgroundColor = [UIColor orangeColor];[self.view addSubview:menuView];
復制代碼
- 方式二:
QiPageMenuView *menuView = [[QiPageMenuView alloc]initWithFrame:CGRectMake(0, 0, self.view.width, 50) titles:@[@"系統消息",@"節日消息",@"廣播通知"]];menuView.backgroundColor = [UIColor orangeColor];//定制樣式menuView.normalTitleColor = [UIColor blackColor];menuView.selectedTitleColor = [UIColor redColor];menuView.titleFont = [UIFont systemFontOfSize:14];menuView.selectedTitleFont = [UIFont systemFontOfSize:14];menuView.itemIsVerticalCentred = YES;menuView.itemTitlePadding = 10.0;menuView.itemTopPadding = 20.0;menuView.itemSpace = 10.0;menuView.leftMargin = 20.0;menuView.rightMargin = 20.0;menuView.itemsAutoResizing = YES;menuView.itemWidth = 90;menuView.itemHeight = 40;menuView.hasUnderLine = YES;menuView.lineColor = [UIColor greenColor];menuView.lineWitdh = 30;menuView.lineHeight = 4.0;menuView.lineTopPadding = 10;[self.view addSubview:menuView];
復制代碼
QiPageContentView實現與使用
- QiPageContentView.h
@interface QiPageContentView : UIView<UIPageViewControllerDelegate, UIPageViewControllerDataSource,UIScrollViewDelegate>@property (nonatomic, strong) UIPageViewController *pageViewController;
@property (nonatomic, strong) NSArray *controllerArray; //!< 控制器數組
/**滑動結束:block回調*/
@property (nonatomic,copy)void(^pageContentViewDidScroll)(NSInteger currentIndex,NSInteger beforeIndex,QiPageContentView *pageView);/**滑動結束:代理回調 若實現block代理不會走*/
@property (nonatomic, weak) id<QiPageContentViewDelegate> contentViewDelgate;/**設置滑動至某一個控制器@param index index@param beforeIndex 控制方向*/
- (void)setPageContentShouldScrollToIndex:(NSInteger)index beforIndex:(NSInteger)beforeIndex;/**初始化方法@param frame frame@param childViewControllers childViewControllers@return 實例*/
- (instancetype)initWithFrame:(CGRect)frame childViewController:(NSArray*)childViewControllers;@end
復制代碼
- QiPageContentView使用
QiPageContentView *contenView = [[QiPageContentView alloc]initWithFrame:CGRectMake(0, 10, self.view.width, self.view.height - 88-10) childViewController:@[ctrl,ctrl1,ctrl2,ctrl3]];
[self.view addSubview:contenView];
復制代碼
QiPageContentView與QiPageMenuView解耦
QiPageContentView與QiPageMenuView使用各自頭文件中定義的協議或者block屬性實現兩者之間事件交互,從而達到分頁聯動效果;兩者的解耦,使得它們的使用更加靈活。
- QiPageMenuView交互事件的傳遞
@protocol QiPageMenuViewDelegate <NSObject>
/**菜單點擊了某個item@param index 點擊了index*/
- (void)pageMenuViewDidClickedIndex:(NSInteger)index beforeIndex:(NSInteger)beforeIndex;@end@interface QiPageMenuView : UIScrollView
/**菜單欄點擊事件:block回調*/
@property (nonatomic,copy)void(^pageItemClicked)(NSInteger clickedIndex,NSInteger beforeIndex,QiPageMenuView *menu);/**菜單欄點擊事件:代理回調 若實現block代理不會走*/
@property (nonatomic, weak) id<QiPageMenuViewDelegate> menuViewDelgate;
復制代碼
- QiPageContentView交互事件的傳遞
@protocol QiPageContentViewDelegate <NSObject>/**滑動完成回調@param index 滑動至index*/
- (void)pageContentViewDidScrollToIndex:(NSInteger)index beforeIndex:(NSInteger)beforeIndex;
@end@interface QiPageContentView : UIView<UIPageViewControllerDelegate, UIPageViewControllerDataSource,UIScrollViewDelegate>
@property (nonatomic, strong) UIPageViewController *pageViewController;
@property (nonatomic, strong) NSArray *controllerArray; //!< 控制器數組
/**滑動結束:block回調*/
@property (nonatomic,copy)void(^pageContentViewDidScroll)(NSInteger currentIndex,NSInteger beforeIndex,QiPageContentView *pageView);
/**滑動結束:代理回調 若實現block代理不會走*/
@property (nonatomic, weak) id<QiPageContentViewDelegate> contentViewDelgate;復制代碼
- QiPageContentView和QiPageMenuView組合實現分頁界面
QiPageMenuView *menuView = [[QiPageMenuView alloc]initWithFrame:CGRectMake(0, 0, self.view.width, 50) titles:@[@"系統消息",@"節日消息",@"廣播通知",@"最新",@"最熱"] dataSource:dataSource];
menuView.backgroundColor = [UIColor orangeColor];
[self.view addSubview:menuView];QiPageContentView *contenView = [[QiPageContentView alloc]initWithFrame:CGRectMake(0, menuView.bottom+10, self.view.width, self.view.height - menuView.bottom - 10 - 88-10) childViewController:@[ctrl,ctrl1,ctrl2,ctrl3,ctrl4]];
[self.view addSubview:contenView];menuView.pageItemClicked = ^(NSInteger clickedIndex, NSInteger beforeIndex, QiPageMenuView *menu) {NSLog(@"點擊了:之前:%ld 現在:%ld",beforeIndex,clickedIndex);[contenView setPageContentShouldScrollToIndex:clickedIndex beforIndex:beforeIndex];};contenView.pageContentViewDidScroll = ^(NSInteger currentIndex, NSInteger beforeIndex, QiPageContentView * _Nonnull pageView) {menuView.pageScrolledIndex = currentIndex;NSLog(@"滾動了:之前:%ld 現在:%ld",beforeIndex,currentIndex);};
復制代碼
工程源碼GitHub地址
小編微信:可加并拉入《QiShare技術交流群》。
關注我們的途徑有:
QiShare(簡書)
QiShare(掘金)
QiShare(知乎)
QiShare(GitHub)
QiShare(CocoaChina)
QiShare(StackOverflow)
QiShare(微信公眾號)
推薦文章:
iOS 中的界面旋轉
iOS 常用布局方式之Frame
iOS 常用布局方式之Autoresizing
iOS 常用布局方式之Constraint
iOS 常用布局方式之StackView
iOS 常用布局方式之Masonry
iOS UIButton根據內容自動布局
奇舞周刊