iOS 快速實現分頁界面的搭建

級別: ★★☆☆☆
標籤:「iOS」「分頁」「QiPageMenuView」
做者: 沐靈洛
審校: QiShare團隊php


iOS 快速實現分頁界面的搭建

項目中咱們常常會遇到滾動分頁的設計效果,被用來對不一樣數據界面的展現進行分類。咱們先能夠來預覽一下實現效果:git

界面展現效果.gif

實現分析

根據動圖進行實現分析:這個效果的實現分爲兩部分頂部的QiPageMenuView和內容展現部分QiPageContentView:github

QiPageMenuView是基於UIScrollView實現的,咱們能夠按照本身的項目需求,定製本身須要實現的效果。QiPageMenuView提供了可設置的屬性有:菜單每一項是否根據文字的大小自適應寬度仍是設置固定寬度、菜單的首項和最後一項距離父視圖的間距、每一項之間的間距、包括了每一項QiPageItem的展現效果自定義等。也實現了菜單項超出一屏幕時自動滑動顯示的效果:數組

QiPageMenuView滑動顯示效果.gif

QiPageContentView是基於UIPageViewController實現的封裝,在項目中可能有多處這樣的界面效果。單純的使用UIPageViewController寫在相應的控制器中,添加相應的子控制器,經過實現UIPageViewController的數據源和代理協議也能夠達到這種效果。可是UIPageViewController嵌套在主控制器中,耦合度比較高,代碼量也比較大,如果多處須要使用這種效果,每次都寫一遍UIPageViewController,效率和可移植性不高。 QiPageMenuView和QiPageContentView之間是解耦合的,彼此均可以做爲單獨的控件去使用。在設計構思時,菜單視圖的樣式有多是QiPageMenuView樣式以外的視圖,如果二者耦合度過高,就變成了QiPageContentView + QiPageMenuView組合,能展示的效果就被限制了。bash

QiPageMenuView實現與使用
  1. 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

複製代碼
  1. 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];
    }
}
複製代碼
  1. 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實現與使用
  1. 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
複製代碼
  1. 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屬性實現二者之間事件交互,從而達到分頁聯動效果;二者的解耦,使得它們的使用更加靈活。微信

  1. 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;
複製代碼
  1. 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;

複製代碼
  1. 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(微信公衆號)ui

推薦文章:
iOS 中的界面旋轉
iOS 經常使用佈局方式之Frame
iOS 經常使用佈局方式之Autoresizing
iOS 經常使用佈局方式之Constraint
iOS 經常使用佈局方式之StackView
iOS 經常使用佈局方式之Masonry
iOS UIButton根據內容自動佈局
奇舞週刊atom

相關文章
相關標籤/搜索