iOS的自定义的滑动菜单控件
ZJQBOSwipeMenuOption.h
// // ZJQBOSwipeMenuOption.h // demo010 // // Created by zhoujianqiang on 15/9/26. // Copyright © 2015年 zhoujianqiang. All rights reserved. // #import <Foundation/Foundation.h> #import <UIKit/UIKit.h> @interface ZJQBOSwipeMenuOption : NSObject @property (nonatomic,assign) CGRect frame;//菜单区视图的frame属性 @property (nonatomic,strong) UIColor* selectedTextColor;//菜单被选中时的文字颜色 @property (nonatomic,strong) UIColor* unselectedTextColor;//菜单未选中时的文字颜色 @property (nonatomic,strong) UIColor* selectedTextBackground;//菜单被选中时的文字背景颜色 @property (nonatomic,strong) UIColor* unselectedTextBackground;//菜单未选中时的文字背景颜色 @property (nonatomic,strong) UIFont* textFont;//菜单字体大小 @property (nonatomic,strong) UIImage* selectedLineImage;//菜单被选中时底线颜色图片 @property (nonatomic,strong) UIImage* unselectedLineImage;//菜单未选中时底线颜色图片 @property (nonatomic,assign) CGFloat lineImageHeight;//菜单底线图片的高度 @property (nonatomic,assign) NSInteger max;//菜单数量显示上限,超过max数量的菜单必须通过左右滑动手势显示 #pragma mark 对象初始化-对象方法 -(instancetype)initWithFrame : (CGRect) frame andWithSelectedTextColor : (UIColor*) selectedTextColor andWithUnselectedTextColor : (UIColor*) unselectedTextColor andWithSelectedTextBackground : (UIColor*) selectedTextBackground andWithUnselectedTextBackground : (UIColor*) unselectedTextBackground andWithTextFont : (UIFont*) textFont andWithSelectedLineImage : (UIImage*) selectedLineImage andWithUnselectedLineImage : (UIImage*) unselectedLineImage andWithLineImageHeight : (CGFloat) lineImageHeight andWithMax : (NSInteger) max; #pragma mark 对象初始化-类方法 +(instancetype)initWithFrame : (CGRect) frame andWithSelectedTextColor : (UIColor*) selectedTextColor andWithUnselectedTextColor : (UIColor*) unselectedTextColor andWithSelectedTextBackground : (UIColor*) selectedTextBackground andWithUnselectedTextBackground : (UIColor*) unselectedTextBackground andWithTextFont : (UIFont*) textFont andWithSelectedLineImage : (UIImage*) selectedLineImage andWithUnselectedLineImage : (UIImage*) unselectedLineImage andWithLineImageHeight : (CGFloat) lineImageHeight andWithMax : (NSInteger) max; @end
ZJQBOSwipeMenuOption.m
// // ZJQBOSwipeMenuOption.m // demo010 // // Created by zhoujianqiang on 15/9/26. // Copyright © 2015年 zhoujianqiang. All rights reserved. // #import "ZJQBOSwipeMenuOption.h" @implementation ZJQBOSwipeMenuOption #pragma mark 对象初始化-对象方法 -(instancetype)initWithFrame : (CGRect) frame andWithSelectedTextColor : (UIColor*) selectedTextColor andWithUnselectedTextColor : (UIColor*) unselectedTextColor andWithSelectedTextBackground : (UIColor*) selectedTextBackground andWithUnselectedTextBackground : (UIColor*) unselectedTextBackground andWithTextFont : (UIFont*) textFont andWithSelectedLineImage : (UIImage*) selectedLineImage andWithUnselectedLineImage : (UIImage*) unselectedLineImage andWithLineImageHeight : (CGFloat) lineImageHeight andWithMax : (NSInteger) max { if (self=[super init]) { _frame=frame; _selectedTextColor=selectedTextColor; _unselectedTextColor=unselectedTextColor; _selectedTextBackground=selectedTextBackground; _unselectedTextBackground=unselectedTextBackground; _textFont=textFont; _selectedLineImage=selectedLineImage; _unselectedLineImage=unselectedLineImage; _lineImageHeight=lineImageHeight; _max=max; } return self; } #pragma mark 对象初始化-类方法 +(instancetype)initWithFrame : (CGRect) frame andWithSelectedTextColor : (UIColor*) selectedTextColor andWithUnselectedTextColor : (UIColor*) unselectedTextColor andWithSelectedTextBackground : (UIColor*) selectedTextBackground andWithUnselectedTextBackground : (UIColor*) unselectedTextBackground andWithTextFont : (UIFont*) textFont andWithSelectedLineImage : (UIImage*) selectedLineImage andWithUnselectedLineImage : (UIImage*) unselectedLineImage andWithLineImageHeight : (CGFloat) lineImageHeight andWithMax : (NSInteger) max { ZJQBOSwipeMenuOption* option = [[ZJQBOSwipeMenuOption alloc] initWithFrame:frame andWithSelectedTextColor:selectedTextColor andWithUnselectedTextColor:unselectedTextColor andWithSelectedTextBackground:selectedTextBackground andWithUnselectedTextBackground:unselectedTextBackground andWithTextFont:textFont andWithSelectedLineImage:selectedLineImage andWithUnselectedLineImage:unselectedLineImage andWithLineImageHeight:lineImageHeight andWithMax:max]; return option; } @end
ZJQBOSwipeMenuOption.h
// // ZJQSwipeMenuView.h // demo010 // // Created by zhoujianqiang on 15/9/26. // Copyright © 2015年 zhoujianqiang. All rights reserved. // #import <UIKit/UIKit.h> #import "ZJQBOSwipeMenuOption.h" @protocol ZJQSwipeMenuViewDelegate <NSObject> -(void)menuClickedAtIndex : (NSInteger) index; @end @interface ZJQSwipeMenuView : UIView @property (nonatomic,strong) NSMutableArray* menus; //菜单数组:字符串数组类型,例如[@"菜单1",@"菜单2",...] @property (nonatomic,strong) ZJQBOSwipeMenuOption* option;//菜单参数 @property (nonatomic,strong) id <ZJQSwipeMenuViewDelegate> delegate;//委托代理 -(instancetype)initWithMenus : (NSMutableArray*) menus andWithOption : (ZJQBOSwipeMenuOption*) option; @end
ZJQSwipeMenuView.m
// // ZJQSwipeMenuView.m // demo010 // // Created by zhoujianqiang on 15/9/26. // Copyright © 2015年 zhoujianqiang. All rights reserved. // #import "ZJQSwipeMenuView.h" #define TAG_MENU 10000 //菜单默认起始tag属性值 #define TAG_LINE 20000 //菜单下划线默认起始tag属性值 @implementation ZJQSwipeMenuView { @private CGFloat menuWidth_;//单个菜单占用的宽度 NSInteger count_;//总的菜单个数 NSInteger showedCount_;//屏幕显示的菜单个数 NSInteger rightIndexInAllMenus_;//最右边菜单索引号,在整个菜单数组中从0开始计数的 BOOL hasUnshowedMenu_;//是否有屏幕未展示的菜单,如果菜单总数超过菜单上限,那么肯定为YES NSInteger selectedMenuIndex_;//当前选择的菜单索引号 CGPoint touchBeginPoint_;//触摸移动开始位置 CGFloat minX_;//菜单区起点x最小值 CGFloat maxX_;//菜单区起点x最大值 } #pragma mark --------> 构造器 <-------- -(instancetype)initWithMenus : (NSMutableArray*) menus andWithOption : (ZJQBOSwipeMenuOption*) option { if (self=[super init]) { _menus=menus; _option=option; [self _init]; } return self; } #pragma mark --------> 初始化 <-------- -(void)_init { [self _initView]; [self _initVariables]; [self _initMenus]; } -(void)_initView{ self.frame=_option.frame;//菜单区frame } -(void)_initVariables{ count_=_menus.count;//总的菜单个数 showedCount_=(count_>_option.max)?_option.max:count_;//屏幕显示的菜单个数,如果超过菜单显示上限,则后面的不显示 CGFloat width = self.frame.size.width;//菜单区的宽度 CGFloat frameX=self.frame.origin.x; menuWidth_=width/showedCount_;//单个菜单占用的宽度 rightIndexInAllMenus_=showedCount_-1;//最右边菜单索引号 selectedMenuIndex_=0;//屏幕上被选中菜单的索引号 hasUnshowedMenu_=(count_>_option.max)?YES:NO;//是否有屏幕未展示的菜单 //重新计算菜单区的宽度 CGFloat newWidth = count_*menuWidth_; newWidth = (newWidth>width)? newWidth:width; CGRect newFrame = self.frame; newFrame.size.width=newWidth; self.frame=newFrame; //计算菜单区起点x坐标的范围 maxX_=frameX; minX_=maxX_+width-self.frame.size.width; } -(void)_initMenus { for (int i=0; i<count_; i++) { // BOOL isSelected = (selectedMenuIndex_==i); [self _addMenu:isSelected andWithIndexInShowedMenus:i andWithIndexInAllMenus:i andIsAppnedAtTail:YES]; } } #pragma mark --------> tag与菜单项的关系操作区 <-------- -(NSInteger) _getMenuTagWithIndexInAllMenus : (NSInteger) indexInAllMenus{ return TAG_MENU+indexInAllMenus; } -(NSInteger) _getIndexInAllMenusWithMenuTag : (NSInteger) menuTag{ return menuTag-TAG_MENU; } -(NSInteger) _getLineTagWithIndexInAllMenus : (NSInteger) indexInAllMenus{ return TAG_LINE+indexInAllMenus; } -(NSInteger) _getIndexInAllMenusWithLineTag : (NSInteger) lineTag{ return lineTag-TAG_LINE; } #pragma mark --------> 添加1项菜单 <-------- -(void)_addMenu : (BOOL) isSelected andWithIndexInShowedMenus : (NSInteger) indexInShowedMenus andWithIndexInAllMenus :(NSInteger) indexInAllMenus andIsAppnedAtTail : (BOOL) isAppendAtTail{ //菜单文字 NSString* menuTitle = _menus[indexInAllMenus]; UILabel* lblTabMenu = [[UILabel alloc]init]; lblTabMenu.frame = CGRectMake(indexInShowedMenus*menuWidth_, 0, menuWidth_, self.frame.size.height-_option.lineImageHeight); lblTabMenu.backgroundColor=(isSelected)?_option.selectedTextBackground:_option.unselectedTextBackground; lblTabMenu.text=menuTitle; lblTabMenu.textColor=(isSelected)?_option.selectedTextColor:_option.unselectedTextColor; lblTabMenu.font=_option.textFont; lblTabMenu.textAlignment=NSTextAlignmentCenter; lblTabMenu.tag=[self _getMenuTagWithIndexInAllMenus:indexInAllMenus]; lblTabMenu.userInteractionEnabled=YES; UITapGestureRecognizer* tapGestureRecognizer = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(_menuClicked:)]; [lblTabMenu addGestureRecognizer:tapGestureRecognizer]; UIImageView* ivLine = [[UIImageView alloc]init]; ivLine.image = (isSelected)?_option.selectedLineImage:_option.unselectedLineImage; ivLine.frame= CGRectMake(indexInShowedMenus*menuWidth_, self.frame.size.height-_option.lineImageHeight, menuWidth_, _option.lineImageHeight); ivLine.tag=[self _getLineTagWithIndexInAllMenus:indexInAllMenus]; if (isAppendAtTail) { [self addSubview:lblTabMenu]; [self addSubview:ivLine]; }else{ //注意插入最前面的时候,顺序要和上面的相反 [self insertSubview:ivLine atIndex:0]; [self insertSubview:lblTabMenu atIndex:0]; } ivLine.alpha=0; [UIView animateWithDuration:0.5 animations:^{ ivLine.alpha = 1; } completion:^(BOOL finished){ }]; } #pragma mark --------> 按钮点击和滑动手势事件区 <-------- -(void) _menuClicked : (UITapGestureRecognizer*) sender { UILabel* lblMenu = (UILabel*)sender.view; NSInteger tag = lblMenu.tag; NSInteger menuIndex = [self _getIndexInAllMenusWithMenuTag:tag]; [self _didSelectMenuAtIndex:menuIndex]; //让delegate去继续执行 if (_delegate) { if ([_delegate respondsToSelector:@selector(menuClickedAtIndex:)]) { [_delegate menuClickedAtIndex:menuIndex]; } } } -(void) _didSelectMenuAtIndex : (NSInteger) menuIndex { //将之前选择的菜单取消选择 NSInteger lblOldMenuTag = [self _getMenuTagWithIndexInAllMenus:selectedMenuIndex_]; UILabel* lblOldMenu = (UILabel*) [self viewWithTag:lblOldMenuTag]; lblOldMenu.backgroundColor=_option.unselectedTextBackground; lblOldMenu.textColor=_option.unselectedTextColor; NSInteger oldLineTag = [self _getLineTagWithIndexInAllMenus:selectedMenuIndex_]; UIImageView* ivOldLine = (UIImageView*) [self viewWithTag:oldLineTag]; ivOldLine.image=_option.unselectedLineImage; //选择当前点中的菜单 NSInteger lblMenuTag = [self _getMenuTagWithIndexInAllMenus:menuIndex]; UILabel* lblMenu = (UILabel*) [self viewWithTag:lblMenuTag]; lblMenu.backgroundColor=_option.selectedTextBackground; lblMenu.textColor=_option.selectedTextColor; NSInteger lineTag = [self _getLineTagWithIndexInAllMenus:menuIndex]; UIImageView* ivLine = (UIImageView*) [self viewWithTag:lineTag]; ivLine.image=_option.selectedLineImage; //更新当前选中的菜单索引 selectedMenuIndex_=menuIndex; } -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ UITouch* touch = [touches anyObject]; touchBeginPoint_=[touch locationInView:self]; [super touchesBegan:touches withEvent:event]; } -(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{ UITouch* touch = [touches anyObject]; CGPoint point = [touch locationInView:self]; //x坐标变化 CGFloat xChanged = point.x-touchBeginPoint_.x; //xChanged<0表示向左移动,xChanged>0表示向右移动 //判断是否可以移动菜单 BOOL isCanMoveMenu = NO; CGFloat newX = self.frame.origin.x+xChanged; isCanMoveMenu = (newX>=minX_ && newX<=maxX_); //修改菜单区frame CGRect newFrame = self.frame; if (isCanMoveMenu) { newFrame.origin.x=newFrame.origin.x+xChanged; }else{ if (xChanged>0) { newFrame.origin.x=maxX_; }else{ newFrame.origin.x=minX_; } } self.frame=newFrame; } @end
调用方法:
ViewController996.h
// // ViewController996.h // demo010 // // Created by zhoujianqiang on 15/9/26. // Copyright © 2015年 zhoujianqiang. All rights reserved. // #import <UIKit/UIKit.h> #import "ZJQBOSwipeMenuOption.h" #import "ZJQSwipeMenuView.h" @interface ViewController996 : UIViewController<ZJQSwipeMenuViewDelegate> @property (nonatomic,strong) ZJQSwipeMenuView* swipeMenuView; @property (nonatomic,strong) ZJQBOSwipeMenuOption* menuOption; @end
ViewController996.m
// // ViewController996.m // demo010 // // Created by zhoujianqiang on 15/9/26. // Copyright © 2015年 zhoujianqiang. All rights reserved. // #import "ViewController996.h" #define IOS7 [[[UIDevice currentDevice]systemVersion] floatValue] >= 7.0 //判断SDK版本号是否是7.0或7。0以上 @interface ViewController996 () @end @implementation ViewController996 #pragma mark ----------> 系统方法区 <---------- - (void)viewDidLoad { [super viewDidLoad]; [self _init]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; } #pragma mark ----------> 初始化 <---------- -(void)_init{ if (IOS7) { self.edgesForExtendedLayout=UIRectEdgeNone; } [email protected]"Swipe Menu"; [self _initPropertys]; } -(void)_initPropertys{ CGRect frame = CGRectMake(0, 0, self.view.frame.size.width, 40); _menuOption = [ZJQBOSwipeMenuOption initWithFrame:frame andWithSelectedTextColor:[UIColor redColor] andWithUnselectedTextColor:[UIColor blackColor] andWithSelectedTextBackground:[UIColor whiteColor] andWithUnselectedTextBackground:[UIColor whiteColor] andWithTextFont:[UIFont systemFontOfSize:13] andWithSelectedLineImage:[UIImage imageNamed:@"r"] andWithUnselectedLineImage:[UIImage imageNamed:@"w"] andWithLineImageHeight:3 andWithMax:6]; NSMutableArray* menus = [[NSMutableArray alloc]init]; for (int i=0; i<10; i++) { [menus addObject:[NSString stringWithFormat:@"菜单%d",i]]; } _swipeMenuView = [[ZJQSwipeMenuView alloc]initWithMenus:menus andWithOption:_menuOption]; [self.view addSubview:_swipeMenuView]; _swipeMenuView.delegate=self; } #pragma mark ----------> 实现ZJQSwipeMenuViewDelegate中的方法 <---------- -(void)menuClickedAtIndex:(NSInteger)index{ NSLog(@"menuClickedAtIndex:index=%ld,%@",index,_swipeMenuView.menus[index]); } @end
效果图:
完整工程代码为附件中的demo010.zip