美文网首页
iOS 快速实现分页界面的搭建。

iOS 快速实现分页界面的搭建。

作者: 沐灵洛 | 来源:发表于2019-08-26 19:41 被阅读0次

    iOS 快速实现分页界面的搭建。

    项目中我们经常会遇到滚动分页的设计效果,被用来对不同数据界面的展示进行分类。我们先可以来预览一下实现效果:

    界面展示效果.gif
    实现分析

    根据动图进行实现分析:这个效果的实现分为两部分顶部的QiPageMenuView和内容展示部分QiPageContentView:

    QiPageMenuView是基于UIScrollView实现的,我们可以按照自己的项目需求,定制自己需要实现的效果。QiPageMenuView提供了可设置的属性有:菜单每一项是否根据文字的大小自适应宽度还是设置固定宽度、菜单的首项和最后一项距离父视图的间距、每一项之间的间距、包括了每一项QiPageItem的展示效果自定义等。也实现了菜单项超出一屏幕时自动滑动显示的效果:

    QiPageMenuView滑动显示效果.gif

    QiPageContentView是基于UIPageViewController实现的封装,在项目中可能有多处这样的界面效果。单纯的使用UIPageViewController写在相应的控制器中,添加相应的子控制器,通过实现UIPageViewController的数据源和代理协议也可以达到这种效果。但是UIPageViewController嵌套在主控制器中,耦合度比较高,代码量也比较大,若是多处需要使用这种效果,每次都写一遍UIPageViewController,效率和可移植性不高。
    QiPageMenuView和QiPageContentView之间是解耦合的,彼此都可以作为单独的控件去使用。在设计构思时,菜单视图的样式有可能是QiPageMenuView样式之外的视图,若是两者耦合度太高,就变成了QiPageContentView + QiPageMenuView组合,能展现的效果就被限制了。

    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
    
    

    2.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;
    

    2.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;
    
    

    3.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地址

    相关文章

      网友评论

          本文标题:iOS 快速实现分页界面的搭建。

          本文链接:https://www.haomeiwen.com/subject/tyaftctx.html