iOS 三级下拉菜单功能实现
程序员文章站
2023-12-20 16:50:52
前言
app 常用控件 -- 多级下拉菜单, 如团购类, 房屋类, 对数据进行筛选. 有一级, 二级, 三级, 再多就不会以这种样式,呈现给用户了. 作者就简单聊一下 多...
前言
app 常用控件 -- 多级下拉菜单, 如团购类, 房屋类, 对数据进行筛选. 有一级, 二级, 三级, 再多就不会以这种样式,呈现给用户了. 作者就简单聊一下 多级下拉菜单
一 目标
- 默认显示一个 tableview, 点击数据后, 添加第二个tableview, 并实现大小变化
- 第二次打开下拉菜单. 保存上次选中数据
二 菜单控件dropmenuview
.h文件
#import <uikit/uikit.h> @class dropmenuview; @protocol dropmenuviewdelegate <nsobject> -(void)dropmenuview:(dropmenuview *)view didselectname:(nsstring *)str; @end @interface dropmenuview : uiview @property (nonatomic, weak) id<dropmenuviewdelegate> delegate; /** 箭头变化 */ @property (nonatomic, strong) uiview *arrowview; /** 控件设置 @param view 提供控件 位置信息 @param tablenum 显示tableview数量 @param arr 使用数据 */ -(void)creatdropview:(uiview *)view withshowtablenum:(nsinteger)tablenum withdata:(nsarray *)arr; /** 视图消失 */ - (void)dismiss; @end
.m文件
#import "dropmenuview.h" #define kwidth [uiscreen mainscreen].bounds.size.width #define kheight [uiscreen mainscreen].bounds.size.height @interface dropmenuview ()<uitableviewdelegate, uitableviewdatasource> { @private /** 保存 选择的数据(行数) */ nsinteger selects[3]; } @property (nonatomic, assign) bool show; // 按钮点击后 视图显示/隐藏 @property (nonatomic, assign) cgfloat rowheightnum; // 设置 rom 高度 /* 底层取消按钮 */ @property (nonatomic, strong) uibutton *cancelbutton; /** 表视图数组 */ @property (nonatomic, strong) nsarray *tableviewarr; /** 表视图的 底部视图 */ @property (nonatomic, strong) uiview *tableviewunderview; /** 显示 tableview 数量 */ @property (nonatomic, assign) nsinteger tablecount; /** 数据 */ @property (nonatomic, strong) nsarray *dataarr; @end @implementation dropmenuview - (instancetype)init { self = [super init]; if (self) { /** 数据初始化 */ self.dataarr = [nsarray array]; /** 保存 初始值为-1 */ for (int i = 0; i < 3; i++) { selects[i] = -1; } /* 底层取消按钮 */ self.cancelbutton = [uibutton buttonwithtype:uibuttontypecustom]; self.cancelbutton.backgroundcolor = [uicolor colorwithwhite:0.0 alpha:0.3]; [self.cancelbutton addtarget:self action:@selector(clickcancelbutton:) forcontrolevents:uicontroleventtouchupinside]; [self addsubview:self.cancelbutton]; /** 表视图的 底部视图初始化 */ self.tableviewunderview = [[uiview alloc] init]; self.tableviewunderview.backgroundcolor = [uicolor colorwithred:0.74 green:0.73 blue:0.76 alpha:1.000]; [self.cancelbutton addsubview:self.tableviewunderview]; /** 默认设置为no, row高度为40 */ self.show = no; self.rowheightnum = 40.0f; } return self; } -(void)creatdropview:(uiview *)view withshowtablenum:(nsinteger)tablenum withdata:(nsarray *)arr{ if (!self.show) { self.show = !self.show; // 显示 tableview数量 self.tablecount = tablenum; // 数据 self.dataarr = arr; for (uitableview *tableview in self.tableviewarr) { [tableview reloaddata]; } // 初始位置 设置 cgfloat x = 0.f; cgfloat y = view.frame.origin.y + view.frame.size.height; cgfloat w = kwidth; cgfloat h = kheight - y; self.frame = cgrectmake(x, y, w, h); self.cancelbutton.frame = cgrectmake(0, 0, self.frame.size.width, self.frame.size.height); self.tableviewunderview.frame = cgrectmake(0, 0, self.frame.size.width, self.rowheightnum * 7); if (!self.superview) { [[[uiapplication sharedapplication] keywindow] addsubview:self]; self.alpha = 0.0f; [uiview animatewithduration:0.2f animations:^{ self.alpha = 1.0f; }]; [self loadselects]; [self adjusttableviews]; } }else{ /** 什么也不选择时候, 再次点击按钮 消失视图 */ [self dismiss]; } } #pragma mark - 加载选中的tableview -(void)loadselects{ [self.tableviewarr enumerateobjectsusingblock:^(uitableview *tableview, nsuinteger idx, bool * _nonnull stop) { // 刷新tableview数据 [tableview reloaddata]; // 选中tableview某一行 [tableview selectrowatindexpath:[nsindexpath indexpathforrow:selects[idx] insection:0] animated:no scrollposition:uitableviewscrollpositionnone]; // 加 !idx 是因为 循环第一次 idx == 0 方法不执行, 所以需要循环一次 加载一个tableview. if((selects[idx] != -1 && !tableview.superview) || !idx) { [self.tableviewunderview addsubview:tableview]; [uiview animatewithduration:0.2 animations:^{ if (self.arrowview) { self.arrowview.transform = cgaffinetransformmakerotation(m_pi); } }]; } }]; } #pragma mark - 重置tableview的 位置 -(void)adjusttableviews{ // 显示的 tableview 数量 int addtablecount = 0; for (uitableview *tableview in self.tableviewarr) { if (tableview.superview) { addtablecount++; } } for (int i = 0; i < addtablecount; i++) { uitableview *tableview = self.tableviewarr[i]; cgrect adjustframe = tableview.frame; adjustframe.size.width = kwidth / addtablecount ; adjustframe.origin.x = adjustframe.size.width * i + 0.5 * i; adjustframe.size.height = self.tableviewunderview.frame.size.height ; tableview.frame = adjustframe; } } #pragma mark - tableview协议 /** 行数 */ -(nsinteger)tableview:(uitableview *)tableview numberofrowsinsection:(nsinteger)section{ nsinteger __block count; [self.tableviewarr enumerateobjectsusingblock:^(id _nonnull obj, nsuinteger idx, bool * _nonnull stop) { if (obj == tableview) { nsinteger firstselectrow = ((uitableview *)self.tableviewarr[0]).indexpathforselectedrow.row ; nsinteger secondselectrow = ((uitableview *)self.tableviewarr[1]).indexpathforselectedrow.row ; count = [self countforchoosetable:idx firsttableselectrow:firstselectrow withsecondtableselectrow:secondselectrow]; } }]; return count; } // 可以将 方法提出来, 如果有需要 可以设置为协议实现封装, 作者仅提取一个, 其他均在 tableview自身协议中写 -(nsinteger)countforchoosetable:(nsinteger)idx firsttableselectrow:(nsinteger)firstselectrow withsecondtableselectrow:(nsinteger)secondselectrow{ if (idx == 0) { return self.dataarr.count; }else if (idx == 1){ if (firstselectrow == -1) { return 0; }else{ if (self.tablecount == 2) { return [self.dataarr[firstselectrow][@"subcategories"] count]; }else{ return [self.dataarr[firstselectrow][@"sub"] count]; } } }else{ if (secondselectrow == -1) { return 0; }else{ return [self.dataarr[firstselectrow][@"sub"][secondselectrow][@"sub"] count]; } } } /** 自定义cell */ -(uitableviewcell *)tableview:(uitableview *)tableview cellforrowatindexpath:(nsindexpath *)indexpath{ uitableviewcell *cell = [tableview dequeuereusablecellwithidentifier:@"dropcell"]; cell.textlabel.font = [uifont systemfontofsize:14]; if (self.tablecount == 1) { cell.textlabel.text = self.dataarr[indexpath.row][@"label"]; }else if (self.tablecount == 2){ nsinteger firstselectrow = ((uitableview *)self.tableviewarr[0]).indexpathforselectedrow.row; if (tableview == self.tableviewarr[0]) { cell.textlabel.text = self.dataarr[indexpath.row][@"name"]; }else if (tableview == self.tableviewarr[1]){ cell.textlabel.text = self.dataarr[firstselectrow][@"subcategories"][indexpath.row]; } }else if (self.tablecount == 3){ nsinteger firstselectrow = ((uitableview *)self.tableviewarr[0]).indexpathforselectedrow.row; nsinteger secondselectrow = ((uitableview *)self.tableviewarr[1]).indexpathforselectedrow.row; if (tableview == self.tableviewarr[0]) { cell.textlabel.text = self.dataarr[indexpath.row][@"name"]; }else if (tableview == self.tableviewarr[1]){ cell.textlabel.text = self.dataarr[firstselectrow][@"sub"][indexpath.row][@"name"]; }else if (tableview == self.tableviewarr[2]){ cell.textlabel.text = self.dataarr[firstselectrow][@"sub"][secondselectrow][@"sub"][indexpath.row]; } } return cell; } /** 点击 */ -(void)tableview:(uitableview *)tableview didselectrowatindexpath:(nsindexpath *)indexpath{ uitableview *secondtableview = self.tableviewarr[1]; uitableview *thirdtableview = self.tableviewarr[2]; if (self.tablecount == 1) { [self saveselects]; [self dismiss]; [_delegate dropmenuview:self didselectname:self.dataarr[indexpath.row][@"label"]]; }else if (self.tablecount == 2){ if (tableview == self.tableviewarr[0]) { if (!secondtableview.superview) { [self.tableviewunderview addsubview:secondtableview]; } [secondtableview reloaddata]; [self adjusttableviews]; }else if (tableview == self.tableviewarr[1]){ [self saveselects]; [self dismiss]; nsinteger firstselectrow = ((uitableview *)self.tableviewarr[0]).indexpathforselectedrow.row; [_delegate dropmenuview:self didselectname:self.dataarr[firstselectrow][@"subcategories"][indexpath.row]]; } }else if (self.tablecount == 3){ nsinteger firstselectrow = ((uitableview *)self.tableviewarr[0]).indexpathforselectedrow.row; nsinteger secondselectrow = ((uitableview *)self.tableviewarr[1]).indexpathforselectedrow.row; if (tableview == self.tableviewarr[0]) { if (!secondtableview.superview) { [self.tableviewunderview addsubview:secondtableview]; } [self adjusttableviews]; [secondtableview reloaddata]; }else if (tableview == self.tableviewarr[1]){ if (!thirdtableview.superview) { [self.tableviewunderview addsubview:thirdtableview]; } [self adjusttableviews]; [thirdtableview reloaddata]; }else if (tableview == self.tableviewarr[2]){ [self saveselects]; [self dismiss]; [_delegate dropmenuview:self didselectname:self.dataarr[firstselectrow][@"sub"][secondselectrow][@"sub"][indexpath.row]]; } } } #pragma mark - 记录 选择状态 -(void)saveselects{ [self.tableviewarr enumerateobjectsusingblock:^(uitableview *tableview, nsuinteger idx, bool * _nonnull stop) { selects[idx] = tableview.superview ? tableview.indexpathforselectedrow.row : -1; }]; } #pragma mark - 视图消失 - (void)dismiss{ if(self.superview) { self.show = !self.show; [self endediting:yes]; [uiview animatewithduration:.25f animations:^{ self.alpha = .0f; } completion:^(bool finished) { [self.tableviewunderview.subviews enumerateobjectsusingblock:^(uiview *obj, nsuinteger idx, bool *stop) { [obj removefromsuperview]; }]; [self removefromsuperview]; [uiview animatewithduration:0.2 animations:^{ if (self.arrowview) { self.arrowview.transform = cgaffinetransformmakerotation(0); } }]; }]; } } /** 底部按钮, 视图消失 */ -(void)clickcancelbutton:(uibutton *)button{ [self dismiss]; } /** 懒加载 */ -(nsarray *)tableviewarr{ if (_tableviewarr == nil) { _tableviewarr = @[[[uitableview alloc] init], [[uitableview alloc] init], [[uitableview alloc] init]]; for (uitableview *tableview in _tableviewarr) { [tableview registerclass:[uitableviewcell class] forcellreuseidentifier:@"dropcell"]; tableview.delegate = self; tableview.datasource = self; tableview.frame = cgrectmake(0, 0, 0, 0); tableview.backgroundcolor = [uicolor whitecolor]; tableview.tablefooterview = [[uiview alloc] init]; tableview.showsverticalscrollindicator = no; tableview.rowheight = self.rowheightnum; } } return _tableviewarr; } @end
三 调用控件menuscreeningview
.h文件
#import <uikit/uikit.h> @interface menuscreeningview : uiview #pragma mark - 筛选菜单消失 -(void)menuscreeningviewdismiss; @end
.m文件
#import "menuscreeningview.h" #import "dropmenuview.h" #define kwidth [uiscreen mainscreen].bounds.size.width #define kheight [uiscreen mainscreen].bounds.size.height @interface menuscreeningview ()<dropmenuviewdelegate> @property (nonatomic, strong) uibutton *onelinkagebutton; @property (nonatomic, strong) uibutton *twolinkagebutton; @property (nonatomic, strong) uibutton *threelinkagebutton; @property (nonatomic, strong) dropmenuview *onelinkagedropmenu; @property (nonatomic, strong) dropmenuview *twolinkagedropmenu; @property (nonatomic, strong) dropmenuview *threelinkagedropmenu; @property (nonatomic, strong) nsarray *addressarr; @property (nonatomic, strong) nsarray *categoriesarr; @property (nonatomic, strong) nsarray *sortsarr; @end @implementation menuscreeningview - (instancetype)initwithframe:(cgrect)frame { self = [super initwithframe:frame]; if (self) { self.onelinkagebutton = [uibutton buttonwithtype:uibuttontypecustom]; self.onelinkagebutton.frame = cgrectmake(0, 0, kwidth/3, 36); [self setupbutton:self.onelinkagebutton withtext:@"一级"]; self.onelinkagedropmenu = [[dropmenuview alloc] init]; self.onelinkagedropmenu.arrowview = self.onelinkagebutton.imageview; self.onelinkagedropmenu.delegate = self; self.twolinkagebutton = [uibutton buttonwithtype:uibuttontypecustom]; self.twolinkagebutton.frame = cgrectmake(kwidth/3, 0, kwidth/3, 36); [self setupbutton:self.twolinkagebutton withtext:@"二级"]; self.twolinkagedropmenu = [[dropmenuview alloc] init]; self.twolinkagedropmenu.arrowview = self.twolinkagebutton.imageview; self.twolinkagedropmenu.delegate = self; self.threelinkagebutton = [uibutton buttonwithtype:uibuttontypecustom]; self.threelinkagebutton.frame = cgrectmake(2 * kwidth/3, 0, kwidth/3, 36); [self setupbutton:self.threelinkagebutton withtext:@"三级"]; self.threelinkagedropmenu = [[dropmenuview alloc] init]; self.threelinkagedropmenu.arrowview = self.threelinkagebutton.imageview; self.threelinkagedropmenu.delegate = self; /** 最下面横线 */ uiview *horizontalline = [[uiview alloc] initwithframe:cgrectmake(0, self.frame.size.height - 0.6, kwidth, 0.6)]; horizontalline.backgroundcolor = [uicolor colorwithwhite:0.8 alpha:1.000]; [self addsubview:horizontalline]; } return self; } #pragma mark - 按钮点击推出菜单 (并且其他的菜单收起) -(void)clickbutton:(uibutton *)button{ if (button == self.onelinkagebutton) { [self.twolinkagedropmenu dismiss]; [self.threelinkagedropmenu dismiss]; [self.onelinkagedropmenu creatdropview:self withshowtablenum:1 withdata:self.sortsarr]; }else if (button == self.twolinkagebutton){ [self.onelinkagedropmenu dismiss]; [self.threelinkagedropmenu dismiss]; [self.twolinkagedropmenu creatdropview:self withshowtablenum:2 withdata:self.categoriesarr]; }else if (button == self.threelinkagebutton){ [self.onelinkagedropmenu dismiss]; [self.twolinkagedropmenu dismiss]; [self.threelinkagedropmenu creatdropview:self withshowtablenum:3 withdata:self.addressarr]; } } #pragma mark - 筛选菜单消失 -(void)menuscreeningviewdismiss{ [self.onelinkagedropmenu dismiss]; [self.twolinkagedropmenu dismiss]; [self.threelinkagedropmenu dismiss]; } #pragma mark - 协议实现 -(void)dropmenuview:(dropmenuview *)view didselectname:(nsstring *)str{ if (view == self.onelinkagedropmenu) { [self.onelinkagebutton settitle:str forstate:uicontrolstatenormal]; [self buttonedgeinsets:self.onelinkagebutton]; }else if (view == self.twolinkagedropmenu){ [self.twolinkagebutton settitle:str forstate:uicontrolstatenormal]; [self buttonedgeinsets:self.twolinkagebutton]; }else if (view == self.threelinkagedropmenu){ [self.threelinkagebutton settitle:str forstate:uicontrolstatenormal]; [self buttonedgeinsets:self.threelinkagebutton]; } } #pragma mark - 设置button -(void)setupbutton:(uibutton *)button withtext:(nsstring *)str{ [button addtarget:self action:@selector(clickbutton:) forcontrolevents:uicontroleventtouchupinside]; [self addsubview:button]; [button settitle:str forstate:uicontrolstatenormal]; button.titlelabel.font = [uifont systemfontofsize:11]; button.titlelabel.linebreakmode = nslinebreakbytruncatingtail; [button settitlecolor:[uicolor colorwithwhite:0.3 alpha:1.000] forstate:uicontrolstatenormal]; [button setimage:[uiimage imagenamed:@"downarr"] forstate:uicontrolstatenormal]; [self buttonedgeinsets:button]; uiview *verticalline = [[uiview alloc]init]; verticalline.backgroundcolor = [uicolor colorwithwhite:0.9 alpha:1.0]; [button addsubview:verticalline]; verticalline.frame = cgrectmake(button.frame.size.width - 0.5, 3, 0.5, 30); } -(void)buttonedgeinsets:(uibutton *)button{ [button settitleedgeinsets:uiedgeinsetsmake(0, -button.imageview.bounds.size.width + 2, 0, button.imageview.bounds.size.width + 10)]; [button setimageedgeinsets:uiedgeinsetsmake(0, button.titlelabel.bounds.size.width + 10, 0, -button.titlelabel.bounds.size.width + 2)]; } #pragma mark - 懒加载 -(nsarray *)addressarr{ if (_addressarr == nil) { nsdictionary *dic = [nsdictionary dictionarywithcontentsoffile:[[nsbundle mainbundle] pathforresource:@"address.plist" oftype:nil]]; _addressarr = dic[@"address"]; } return _addressarr; } -(nsarray *)categoriesarr{ if (_categoriesarr == nil) { _categoriesarr = [nsarray arraywithcontentsoffile:[[nsbundle mainbundle] pathforresource:@"categories.plist" oftype:nil]]; } return _categoriesarr; } -(nsarray *)sortsarr{ if (_sortsarr == nil) { _sortsarr = [nsarray arraywithcontentsoffile:[[nsbundle mainbundle] pathforresource:@"sorts.plist" oftype:nil]]; } return _sortsarr; } @end
四 调用
menuscreeningview *menuscreening = [[menuscreeningview alloc] initwithframe:cgrectmake(0, 64, kwidth, 36)]; [self.view addsubview:menuscreening]; menuscreening.backgroundcolor = [uicolor whitecolor];
五 效果图
六 demo下载
因为数据源 无法上次上传[简书], 所以上传个demo, 细节方面, 可能有未注意地方,仅供参考.
传送门 : linkagemenu_jb51.rar
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。