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

级别: ★★☆☆☆
标签:「iOS」「分页」「QiPageMenuView」
作者: 沐灵洛
审校: QiShare团队


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

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

实现分析

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

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

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复制代码
  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(微信公众号)

推荐文章:
iOS 中的界面旋转
iOS 常用布局方式之Frame
iOS 常用布局方式之Autoresizing
iOS 常用布局方式之Constraint
iOS 常用布局方式之StackView
iOS 常用布局方式之Masonry
iOS UIButton根据内容自动布局
奇舞周刊

转载于:https://juejin.im/post/5d0779595188251c9d45e9e9

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/251636.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java中String的常用方法

java中String的常用方法 转自&#xff1a;http://archer-zhou.iteye.com/blog/443864 java中String的常用方法1、length() 字符串的长度例&#xff1a;char chars[]{a,b.c};String snew String(chars);int lens.length();2、charAt() 截取一个字符例&#xff1a;char ch;ch&quo…

笔试小结---非对称加密算法

非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥(privatekey).公开密钥和私有密钥是一对,如果公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥进行加密,那么只有用对应的公开密钥才能解密. 非对称加密算法的保密性比较好,它消除了最终用户交换…

登录令牌 Token 介绍

Token值介绍 token 值: 登录令牌.利用 token 值来判断用户的登录状态.类似于 MD5 加密之后的长字符串. 用户登录成功之后,在后端(服务器端)会根据用户信息生成一个唯一的值.这个值就是 token 值. 基本使用: 在服务器端(数据库)会保存这个 token 值,以后利用这个 token 值来检索…

java-number

通常&#xff0c;当我使用number类型的时候&#xff0c;我们可以使用原始数据类型例如byte&#xff0c;int,long,double等 int i 5000; float b 13.65; double m 0xaf; 所有包装类&#xff08;整型&#xff0c;长型&#xff0c;字节型&#xff0c;双精度型&#xff0c;浮点型&a…

您的浏览器没有获得Java Virtual Machine(JVM)支持。可能由于没有安装JVM或者已安装但是没有启用。请安装JVM1.5或者以上版本,如果已安装则启用它。...

您的浏览器没有获得Java Virtual Machine(JVM)支持。可能由于没有安装JVM或者已安装但是没有启用。请安装JVM1.5或者以上版本&#xff0c;如果已安装则启用它。 https://www.java.com/zh_CN/download/faq/remove_olderversions.xml https://jingyan.baidu.com/article/6d704a13…

指令定义

restict&#xff1a;它告诉AngularJS这个指令在DOM中可以以何种形式被声明。 E(元素&#xff09; <my-directive> </mydirective>A(属性) <div my-directive“expression”> </div>C(类名) <div class“my-directive:expression;”> </div>…

MyBatis学习总结(9)——使用MyBatis Generator自动创建代码

2019独角兽企业重金招聘Python工程师标准>>> 由于MyBatis属于一种半自动的ORM框架&#xff0c;所以主要的工作就是配置Mapping映射文件&#xff0c;但是由于手写映射文件很容易出错&#xff0c;所以可利用MyBatis生成器自动生成实体类、DAO接口和Mapping映射文件。这…

[BZOJ2125]最短路(圆方树DP)

题意&#xff1a;仙人掌图最短路。 算法&#xff1a;圆方树DP&#xff0c;$O(n\log nQ\log n)$ 首先建出仙人掌圆方树&#xff08;与点双圆方树的区别在于直接连割边&#xff0c;也就是存在圆圆边&#xff09;&#xff0c;然后考虑点u-v的最短路径&#xff0c;显然就是&#xf…

20162317 2017-2018-1 《程序设计与数据结构》第8周学习总结

20162317 2017-2018-1 《程序设计与数据结构》第8周学习总结 教材学习内容总结 1、二叉查找树的定义、性质2、向二叉查找树中添加元素的方法3、在二叉查找树中删除元素的方法4、旋转的定义、方法、意义 教材学习中的问题和解决过程问题1&#xff1a;我在17章中看到这么一句话&a…

ES6模块的转码

浏览器目前还不支持ES6模块,为了实现立刻使用,我们可以将其转为ES5的写法.除了Babel可以用来转码,还有以下两个方法也可以用来转码: ES6 moudule transpilerSystemJS ES6 moudule transpiler是square公司开源的一个转码器,可以将ES6模块转为CommonJS模块或AMD模块,从而在浏览器…

Java基础学习总结(22)——异常处理

2019独角兽企业重金招聘Python工程师标准>>> 一、异常的概念 异常指的是运行期出现的错误&#xff0c;也就是当程序开始执行以后执行期出现的错误。出现错误时观察错误的名字和行号最为重要。 1 package cn.javastudy.summary;2 3 public class TestEx{4 5 …

XAML中格式化日期

要求被格式化数据的类型是DateTime StringFormatyyyy-MM-dd StringFormat{}{0:yyyy-MM-dd}转载于:https://www.cnblogs.com/changbaishan/p/9144584.html

130242014045 林承晖 第2次实验

软件体系结构的第二次实验&#xff08;解释器风格与管道过滤器风格&#xff09; 一、实验目的 1&#xff0e;熟悉体系结构的风格的概念 2&#xff0e;理解和应用管道过滤器型的风格。 3、理解解释器的原理 4、理解编译器模型 二、实验环境 硬件&#xff1a; 软件&#xff1a;P…

AnularJS1事件

在Web应用的组件是松耦合的情况下&#xff0c;比如需要用户验证然后处理授权&#xff0c;即时的通信不总是可行的&#xff0c;因为组件没有耦合在一起。 例如&#xff0c;如果后端对一个请求返回了状态码401&#xff08;表明一个未经授权的请求&#xff09;&#xff0c;我们期望…

Java基础学习总结(8)——super关键字

2019独角兽企业重金招聘Python工程师标准>>> 一、super关键字 在JAVA类中使用super来引用父类的成分&#xff0c;用this来引用当前对象&#xff0c;如果一个类从另外一个类继承&#xff0c;我们new这个子类的实例对象的时候&#xff0c;这个子类对象里面会有一个父类…

conda镜像

转自https://blog.csdn.net/guilutian0541/article/details/81004769 conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main conda config --set show…

Java基础学习总结(17)——线程

2019独角兽企业重金招聘Python工程师标准>>> 一、线程的基本概念 线程理解&#xff1a;线程是一个程序里面不同的执行路径 每一个分支都叫做一个线程&#xff0c;main()叫做主分支&#xff0c;也叫主线程。 程只是一个静态的概念&#xff0c;机器上的一个.class文件…

(转)MySQL自带的性能压力测试工具mysqlslap详解

mysqlslap 是 Mysql 自带的压力测试工具&#xff0c;可以模拟出大量客户端同时操作数据库的情况&#xff0c;通过结果信息来了解数据库的性能状况 mysqlslap 的一个主要工作场景就是对数据库服务器做基准测试 例如我们拿到了一台服务器&#xff0c;准备做为数据库服务器&#x…

node.js HelloWord

创建 server.js var http require("http"); http.createServer(function(req,res){ //设置请求头的编码格式 res.writeHead(200,{Content-Type:text/html;charsetutf-8}); //设置网页的编码格式&#xff08;防止中文乱码&#xff09; res.write("<head>&…

JavaScript --- this

介绍: this:引用环境执行的环境对象arguments:一个类数组对象,它包含传入函数的所以参数callee:arguments对象的一个属性,该属性是一个指针,指向拥有arguments对象的函数caller:保存着调用当前函数的函数引用apply()方法:第一个参数是作用域&#xff0c;第二个参数是Array实例…