最近做项目时遇到一些问题,就是项目里原有分段控制器的适用范围有些局限,虽然网上也有很多分段控制器的demo,但自己写的,可控性和项目适用性自己能很明白,所以我专门封装这样一个分段控制器,解决不同场景下的功能需求。
首先,介绍一下分段控制器,相信大家也都见过这样的场景
image.png
好,看到上图大家应该心里或多或少知道接下来要干的事了,我们一步一步分析如何封装一个好的分段控制器:
1.满足可配置多个子控制器
2.可配置菜单栏各个属性,如字体大小颜色等
3.指示条可配置
4.最好能扩展菜单栏,如最多展示5个,多于5个可左右滑动
5.点击和滑动到某一界面,要知道这是哪个界面
列出来需求,要实现也变得简单了许多:
我们在.h里写出可配置的属性,并写好确定当前是哪个界面的代理
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@protocol SYPageControlIndexDelegate <NSObject>
@optional
-(void)pageIndexDidChange:(NSUInteger)index;
@end
@interface SYPageControlView : UIView<UIScrollViewDelegate>
//设置菜单栏高度
@property (nonatomic,assign) NSInteger btnViewHeight;
//设置按钮下划线宽度
@property (nonatomic,assign) NSInteger indicatorWidth; //默认50,我们最好设置一下
//设置按钮下划线高度(默认1)
@property (nonatomic,assign) NSInteger indicatorHeight;
//设置最大菜单展示个数,菜单多于最大则可滑动 (默认是childVCs的个数)
@property (nonatomic,assign) NSInteger maxMenuCount;
@property (nonatomic,weak) id <SYPageControlIndexDelegate> delegate;
/**
当前选中标题索引,默认0
*/
@property (nonatomic, assign) NSInteger selectIndex;
/**
标题字体大小,默认15
*/
@property (nonatomic, strong) UIFont *titleFont;
/**
标题选中字体大小,默认15
*/
@property (nonatomic, strong) UIFont *titleSelectFont;
/**
标题正常颜色,默认black
*/
@property (nonatomic, strong) UIColor *titleNormalColor;
/**
标题选中颜色,默认red
*/
@property (nonatomic, strong) UIColor *titleSelectColor;
/**
指示器颜色,默认与titleSelectColor一样(注:如不需要,可设置透明色,也可内部去掉这个控件)
*/
@property (nonatomic, strong) UIColor *indicatorColor;
-(instancetype)initWithFrame:(CGRect)frame controllertitles:(NSArray *)titles childViewControllers:(NSArray *)childVCs delegate:(id <SYPageControlIndexDelegate>)delegate;
@end
然后我们在.m实现里实现我们这些功能
1.内部扩展一些属性和View以便使用
@interface SYPageControlView ()
{
UIButton *_seletedBtn;
float menuBtnWidth;
NSMutableArray *titleBtnArr;
}
@property (nonatomic,strong) UIScrollView *pageScroll; //内容
@property (nonatomic,copy) NSArray *viewControllers;
@property (nonatomic,strong) UIView *indicatorView;
@property (nonatomic,strong) UIScrollView *btnView; //可滑动的
@property (nonatomic,copy) NSArray *titleArray;
@end
2.实现初始化方法
-(instancetype)initWithFrame:(CGRect)frame controllertitles:(NSArray *)titles childViewControllers:(NSArray *)childVCs delegate:(id <SYPageControlIndexDelegate>)delegate{
self = [super initWithFrame:frame];
if (self) {
self.titleArray = titles;
self.viewControllers = childVCs;
self.delegate = delegate;
self.maxMenuCount = childVCs.count;
menuBtnWidth = self.width/childVCs.count;
//设置默认属性
[self initWithProperty];
//创建子视图
[self createSubViews];
}
return self;
}
3.设置默认属性和创建子视图
//初始化默认属性值
- (void)initWithProperty
{
self.selectIndex = 0;
self.titleNormalColor = [UIColor blackColor];
self.titleSelectColor = [UIColor redColor];
self.titleFont = [UIFont systemFontOfSize:15];
self.indicatorWidth = 50;
self.indicatorHeight = 1;
self.indicatorColor = self.titleSelectColor;
self.titleSelectFont = self.titleFont;
self.btnViewHeight = 42;
}
-(void)createSubViews{
//1.头部titles
_btnView = [[UIScrollView alloc]init];
_btnView.frame = CGRectMake(0,0,SCREEN_WIDTH,self.btnViewHeight);
_btnView.showsHorizontalScrollIndicator = NO;
_btnView.backgroundColor = [UIColor whiteColor];
_btnView.contentSize = CGSizeMake(menuBtnWidth*self.titleArray.count, self.btnViewHeight);
[self addSubview:_btnView];
//2.标题按钮
titleBtnArr = [[NSMutableArray alloc]init];
for (int i = 0; i < self.titleArray.count; i++) {
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
btn.tag = i+10;
[btn setTitle:self.titleArray[i] forState:UIControlStateNormal];
[btn setTitleColor:self.titleNormalColor forState:UIControlStateNormal];
btn.titleLabel.font = self.titleFont;
btn.frame = CGRectMake(menuBtnWidth*i, 0, menuBtnWidth, self.btnViewHeight);
[_btnView addSubview:btn];
if (i==self.selectIndex) {
_seletedBtn = btn;
_seletedBtn.titleLabel.font = self.titleSelectFont;
[_seletedBtn setTitleColor:self.titleSelectColor forState:UIControlStateNormal];
}
[btn addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchUpInside];
[titleBtnArr addObject:btn];
}
//3.下划线
self.indicatorView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, self.indicatorWidth, self.indicatorHeight)];
self.indicatorView.backgroundColor = self.indicatorColor;
self.indicatorView.center = CGPointMake(menuBtnWidth/2, self.btnViewHeight-self.indicatorHeight/2-1);//底部留1
[_btnView addSubview:self.indicatorView];
//4.内容VC
self.pageScroll = [[UIScrollView alloc] init];
self.pageScroll.delegate = self;
self.pageScroll.pagingEnabled = YES;
self.pageScroll.bounces = NO;
self.pageScroll.frame = CGRectMake(0,self.btnViewHeight, self.width, self.height - self.btnViewHeight);
self.pageScroll.showsHorizontalScrollIndicator = NO;
self.pageScroll.contentSize = CGSizeMake(self.width*self.viewControllers.count, self.pageScroll.height);
self.pageScroll.contentOffset = CGPointMake(self.width*self.selectIndex, 0); //根据默认index设置偏移量
[self addSubview:self.pageScroll];
for (int i = 0; i<self.viewControllers.count; i++) {
UIViewController *vc = self.viewControllers[i];
vc.view.frame = CGRectMake(i*self.width, 0, self.width, self.pageScroll.height);
[self.pageScroll addSubview:vc.view];
}
}
设置默认属性没什么好说的,创建子视图里有几点需要注意,一是标题按钮的创建,二是设置指示器的位置,三是配置多个内容的控制器视图,仔细看代码,逻辑其实也很简单明了
4.实现按钮点击事件和视图滑动事件
#pragma mark - 事件
-(void)btnClick:(UIButton *)sender{
if (_seletedBtn==sender) {
return;
}
if (self.delegate && [self.delegate respondsToSelector:@selector(pageIndexDidChange:)]) {
[self.delegate pageIndexDidChange:sender.tag-10];
}
for (UIButton *btn in titleBtnArr) {
btn.titleLabel.font = self.titleFont;
[btn setTitleColor:self.titleNormalColor forState:UIControlStateNormal];
}
[UIView animateWithDuration:0.3 animations:^{
self.indicatorView.center = CGPointMake(sender.center.x, self.btnViewHeight-self.indicatorHeight/2-1);//底部留1
//想点击没有动画就把这个移到下面
self.pageScroll.contentOffset = CGPointMake((sender.tag-10)*self.width, 0);
[self.pageScroll setContentOffset:CGPointMake((sender.tag-10)*self.width, 0) animated:NO];
}];
sender.titleLabel.font = self.titleSelectFont;
[sender setTitleColor:self.titleSelectColor forState:UIControlStateNormal];
_seletedBtn = sender;
}
#pragma mark - scrollViewDelegate
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
float index = scrollView.contentOffset.x/scrollView.frame.size.width;
if (self.delegate && [self.delegate respondsToSelector:@selector(pageIndexDidChange:)]) {
[self.delegate pageIndexDidChange:index];
}
UIButton *btn = (UIButton *)[self.btnView viewWithTag:index+10];
if (_seletedBtn != btn) {
[UIView animateWithDuration:0.3 animations:^{
self.indicatorView.center = CGPointMake(btn.center.x, self.btnViewHeight-self.indicatorHeight/2-1);//底部留1
}];
_seletedBtn.titleLabel.font = self.titleFont;
[_seletedBtn setTitleColor:self.titleNormalColor forState:UIControlStateNormal];
_seletedBtn = btn;
_seletedBtn.titleLabel.font = self.titleSelectFont;
[_seletedBtn setTitleColor:self.titleSelectColor forState:UIControlStateNormal];
}
}
我们在内部处理了界面偏移和按钮状态更改,如外面有代理,也会将当前第几个页面的index回调给代理,外部逻辑处理会变得很简单
5.个性配置,即设置属性更改UI
我们实现属性的set方法,在set方法内部更改UI
#pragma mark - set方法
-(void)setBtnViewHeight:(NSInteger)btnViewHeight{
_btnViewHeight = btnViewHeight;
for (UIButton *btn in titleBtnArr) {
btn.height = self.btnViewHeight;
}
_btnView.contentSize = CGSizeMake(menuBtnWidth*self.titleArray.count, btnViewHeight);
self.indicatorView.center = CGPointMake(_seletedBtn.center.x, self.btnViewHeight-self.indicatorHeight/2-1);//底部留1
self.pageScroll.frame = CGRectMake(0,self.btnViewHeight, self.width, self.height - self.btnViewHeight);
for (int i = 0; i<self.viewControllers.count; i++) {
UIViewController *vc = self.viewControllers[i];
vc.view.frame = CGRectMake(i*self.width, 0, self.width, self.pageScroll.height);
}
}
-(void)setTitleNormalColor:(UIColor *)titleNormalColor{
_titleNormalColor = titleNormalColor;
for (UIButton *btn in titleBtnArr) {
[btn setTitleColor:titleNormalColor forState:UIControlStateNormal];
}
[_seletedBtn setTitleColor:self.titleSelectColor forState:UIControlStateNormal];
}
-(void)setTitleSelectColor:(UIColor *)titleSelectColor{
_titleSelectColor = titleSelectColor;
[_seletedBtn setTitleColor:titleSelectColor forState:UIControlStateNormal];
}
-(void)setTitleFont:(UIFont *)titleFont{
_titleFont = titleFont;
for (UIButton *btn in titleBtnArr) {
btn.titleLabel.font = titleFont;
}
_seletedBtn.titleLabel.font = self.titleSelectFont;
}
-(void)setTitleSelectFont:(UIFont *)titleSelectFont{
_titleSelectFont = titleSelectFont;
_seletedBtn.titleLabel.font = titleSelectFont;
}
-(void)setSelectIndex:(NSInteger)selectIndex{
_selectIndex = selectIndex;
self.pageScroll.contentOffset = CGPointMake(self.width*selectIndex, 0);
if (self.delegate && [self.delegate respondsToSelector:@selector(pageIndexDidChange:)]) {
[self.delegate pageIndexDidChange:selectIndex];
}
UIButton *btn = (UIButton *)[self.btnView viewWithTag:selectIndex+10];
if (_seletedBtn != btn) {
[UIView animateWithDuration:0.3 animations:^{
self.indicatorView.center = CGPointMake(btn.center.x, self.btnViewHeight-self.indicatorHeight/2-1);//底部留1
}];
_seletedBtn.titleLabel.font = self.titleFont;
[_seletedBtn setTitleColor:self.titleNormalColor forState:UIControlStateNormal];
_seletedBtn = btn;
_seletedBtn.titleLabel.font = self.titleSelectFont;
[_seletedBtn setTitleColor:self.titleSelectColor forState:UIControlStateNormal];
}
}
-(void)setIndicatorColor:(UIColor *)indicatorColor{
_indicatorColor = indicatorColor;
self.indicatorView.backgroundColor = indicatorColor;
}
-(void)setIndicatorWidth:(NSInteger)indicatorWidth{
_indicatorWidth = indicatorWidth;
self.indicatorView.frame = CGRectMake(0, 0, indicatorWidth, self.indicatorHeight);
self.indicatorView.center = CGPointMake(_seletedBtn.center.x, self.btnViewHeight-self.indicatorHeight/2-1);//底部留1
}
-(void)setIndicatorHeight:(NSInteger)indicatorHeight{
_indicatorHeight = indicatorHeight;
self.indicatorView.frame = CGRectMake(0, 0, self.indicatorWidth, indicatorHeight);
self.indicatorView.center = CGPointMake(_seletedBtn.center.x, self.btnViewHeight-self.indicatorHeight/2-1);//底部留1
}
-(void)setMaxMenuCount:(NSInteger)maxMenuCount{
_maxMenuCount = maxMenuCount;
if (self.titleArray.count>maxMenuCount) {
menuBtnWidth = self.width/maxMenuCount;
_btnView.contentSize = CGSizeMake(menuBtnWidth*self.titleArray.count, self.btnViewHeight);
for (int i=0; i<titleBtnArr.count; i++) {
UIButton *btn = titleBtnArr[i];
btn.frame = CGRectMake(menuBtnWidth*i, 0, menuBtnWidth, self.btnViewHeight);
}
}
}
到此,自定义分段控制器就已经封装完了,我们来测试一下
NSArray *titles = @[@"First",@"Second",@"Third"];
FirstViewController *firstVC = [[FirstViewController alloc]init];
SecondViewController *secondVC = [[SecondViewController alloc]init];
ThirdViewController *thirdVC = [[ThirdViewController alloc]init];
NSArray *controllers = @[firstVC,secondVC,thirdVC];
SYPageControlView *pageControlView = [[SYPageControlView alloc]initWithFrame:CGRectMake(0, 64, SCREEN_WIDTH, SCREEN_HEIGHT-64) controllertitles:titles childViewControllers:controllers delegate:self];
[self.view addSubview:pageControlView];
image.png
再修改属性来测试:
pageControlView.maxMenuCount = 2;
pageControlView.selectIndex = 1;
pageControlView.titleSelectFont = [UIFont systemFontOfSize:17];
pageControlView.titleNormalColor = [UIColor grayColor];
pageControlView.titleSelectColor = [UIColor brownColor];
pageControlView.indicatorWidth = SCREEN_WIDTH/2;
pageControlView.indicatorColor = [UIColor greenColor];
image.png
属性已经全部生效,头部菜单2个并且可滑动。
注:以上代码就是按顺序来的所有代码,其中用到了一个UIView的扩展文件,可直接访问UIView的left、width等属性。布局也使用frame,大家能更清楚逻辑。
如果使用还不方便,可以去github上下载代码,也欢迎大家提出建议:
https://github.com/qingmomo/SYPageControlView
网友评论