关键词:递归 多级菜单 复选

目标:

1.显示多级菜单,默认显示一级.

2.可以通过点击有子级的行展开菜单

3.通过复选框,改变选中状态。状态有全选、半选、未选中

4.可以扩展获取当前所选的条目集合

menu.gif

实现过程:

数据处理

1.首先根Datasource进行数据处理

2.生成一个handler:MultilevelDataHandler 将数据处理逻辑在handle处理,将数据处理隔离

MultilevelDataHandler *dataHandler = [MultilevelDataHandler sharedHandler];

[dataHandler setLevelKeys:@[@"second_category", @"knowledge"]]; //由于源数据中每层数据可能采用不同的key,所有我把每层的key依次添加到数组里面,以便数据转化

[dataHandler setReDataSource:dataSource]; // 将源数据交给handle处理

3.建立一个数据模型,需要用一些属性记录层级关系。最后我用了一个字典来记录原始的数据信息。

//

// MultilevelMenuModel.h

// MultilevelMenuWithCheckbox

Created by hyt on 2017/10/31.

// Copyright © 2017年 hyt. All rights reserved.

//#importtypedef NS_ENUM (NSInteger, MMSelectState){

selectNone,

selectHalf,

selectAll

};

@interface MultilevelMenuModel : NSObject

@property (nonatomic, assign) NSInteger MMLevel;

@property (nonatomic, assign) NSInteger MMIndex;

//@property (nonatomic, assign) NSInteger MMSuperIndex;

@property (nonatomic, strong) NSMutableArray *locationArray;

@property (nonatomic, strong) NSArray *MMSubArray;

@property (nonatomic, assign) MMSelectState MMSelectState;

@property (nonatomic, assign) BOOL MMIsOpen;

@property (nonatomic, strong) NSDictionary *dataDict; // original data

@end

这里是Demo的数据Json

json数据:

[{

"id": "",

"name": "",

"type": "",

"second_category": [{

"id": "",

"name": "",

"type": "",

"knowledge": [{

"id": "",

"name": "",

"type": ""

}]

}]

}]

4.将jsonDictionary转化成数据模型的时候,把层级关系也一并赋值。

由于数据层级数量的不确定性,这里用递归的方式把每层的数据结构都放到其父类的subArray当中。

- (MultilevelMenuModel *)getModelByDict:(NSDictionary *)dict

modelLevel:(NSInteger)level

modelIndex:(NSInteger)index

superIndex:(NSInteger)superIndex

locationArray:(NSMutableArray *)loactionArray{

MultilevelMenuModel *levelModel = [[MultilevelMenuModel alloc] init];

levelModel.MMLevel = level;

levelModel.MMIndex = index++;

// levelModel.MMSuperIndex = superIndex;

levelModel.dataDict = [NSDictionary dictionaryWithDictionary:dict];

levelModel.locationArray = [NSMutableArray arrayWithArray:loactionArray];

[levelModel.locationArray addObject:[NSNumber numberWithInteger:superIndex]];

if ([self checkModelHasSubArray:levelModel]) {

[self setModelSubArray:levelModel];

}

return levelModel;

}

- (void)setModelSubArray:(MultilevelMenuModel *)model {

NSString *key = [self getSubKeyByModel:model];

NSArray *subDictArray = model.dataDict[key];

model.MMSubArray = [self getModelArrayFromDictArray:subDictArray

modelLevel:model.MMLevel + 1

superIndex:model.MMIndex

locationArray:model.locationArray];

}

- (NSArray *)getModelArrayFromDictArray:(NSArray *)dictArray

modelLevel:(NSInteger)level

superIndex:(NSInteger)superIndex

locationArray:(NSMutableArray *)locationArray{

NSMutableArray *deArray = [NSMutableArray array];

NSInteger index = 0;

for (NSDictionary *dict in dictArray) {

MultilevelMenuModel *levelModel = [self getModelByDict:dict

modelLevel:level

modelIndex:index++

superIndex:superIndex

locationArray:locationArray];

[deArray addObject:levelModel];

}

return [NSArray arrayWithArray:deArray];

}

5.建一个新的数组用来存储要在tableView上展示的数据模型,按照父类子类,父类子类的顺序排列。我这里默认是把第一级全部关闭展示的

6.实现菜单展开关闭功能

根据点击的model的isOpen属性来判断是否展开。

展开时是把subArray中的子级添加到showData当中去,需注意判断子级当前的状态是否是已展开的,如果是的话需要递归调用展开方法。

关闭时是把subArray中的子级从showData中删除,也用递归的方法把子类的子类也一并删除。

删除子级的时候不影响子级的展开状态。

- (void)addSubModelToShowByModel:(MultilevelMenuModel *)superModel {

NSInteger superIndex = [self.showData indexOfObject:superModel]; //

for (MultilevelMenuModel *subModel in [[superModel.MMSubArray reverseObjectEnumerator] allObjects]) {

if (![self.showData containsObject:subModel]) {

[self.showData insertObject:subModel atIndex:superIndex + 1];

}

if (subModel.MMIsOpen == YES && subModel.MMSubArray.count > 0 ) {

[self addSubModelToShowByModel:subModel];

}

}

superModel.MMIsOpen = YES;

}

- (void)removeSubModelFromShowByModel:(MultilevelMenuModel *)superModel

closeIt:(BOOL)close{

if (superModel.MMSubArray.count == 0) {

return;

}

for (MultilevelMenuModel *subModel in superModel.MMSubArray) {

[self removeSubModelFromShowByModel:subModel closeIt:!subModel.MMIsOpen];

[self.showData removeObject:subModel];

}

superModel.MMIsOpen = !close;

}

7.实现复选框功能

用三个方法分别实现复修改选框的三种状态

每个方法中,都需要考虑当前model的状态改变对其父级与子级的影响

用model的subArray找到其子级,用locationArray记录的坐标找到其父类

父级和子级的状态改变也会影响到他们的父级和子级,所有用递归的方式修改状态

- (void)setModelToBeStateNone:(MultilevelMenuModel *)model {

// make current model unselected

model.MMSelectState = selectNone;

// make sublevel unselected

if (model.MMSubArray.count > 0) {

for (MultilevelMenuModel *subModel in model.MMSubArray) {

if (subModel.MMSelectState != selectNone) {

[self setModelToBeStateNone:subModel];

}

}

}

//make super model unselected

MultilevelMenuModel *supModel = [self findSuperModelBySubModel:model];

if (supModel == nil) {

return;

}

if ([self checkSubModeHasStateAll:supModel]) {

[self setModelToBeStateHalf:supModel];

} else if ([self checkSubModelHasSelected:supModel]) {

[self setModelToBeStateHalf:supModel];

} else {

supModel.MMSelectState = selectNone;

}

}

- (void)setModelToBeStateHalf:(MultilevelMenuModel *)model {

// make current model selected

model.MMSelectState = selectHalf;

//make super model half-selected

MultilevelMenuModel *supModel = [self findSuperModelBySubModel:model];

if (supModel == nil) {

return;

}

[self setModelToBeStateHalf:supModel];

}

- (void)setModelToBeStateAll:(MultilevelMenuModel *)model {

// make current model selected

model.MMSelectState = selectAll;

// make sublevel selected

if (model.MMSubArray.count > 0) {

for (MultilevelMenuModel *subModel in model.MMSubArray) {

if (subModel.MMSelectState != selectAll) {

[self setModelToBeStateAll:subModel];

}

}

}

//make super model selected

MultilevelMenuModel *supModel = [self findSuperModelBySubModel:model];

if (supModel == nil) {

return;

}

if ([self checkSubModelsBeStateAll:supModel]) {

[self setModelToBeStateAll:supModel];

} else {

[self setModelToBeStateHalf:supModel];

}

}

根据locationArray里记录的每一层父级的序号,找到当前model的父级

- (MultilevelMenuModel *)findSuperModelBySubModel:(MultilevelMenuModel *)subModel {

NSArray *location = subModel.locationArray;

if (location.count == 0) {

return nil;

}

MultilevelMenuModel *resultModel;

NSArray *dataSource = self.modelArray;

int i = 1;

while (i < location.count) {

int index = [location[i] intValue];

resultModel = dataSource[index];

dataSource = resultModel.MMSubArray;

i++;

}

return resultModel;

}

小结

由于层级数量的不确定性,所以多次使用到了递归的方式。要注意递归的结束条件,必须陷入死循环当中。

附上本文的Demo

vant树型菜单多级_iOS 动态树形结构 - 实现多级菜单,附带复选框功能相关推荐

  1. QT笔记- QTreeView设置三态setAutoTristate() 树形视图自动复选框——源码分享

    说明 Qt中函数QStandardItem::setAutoTristate()无实际功能,仅作为一个布尔标记.若要实现自动三态复选框功能,需要自行代码构建. 本文通过编写两个派生类,完成了这个功能. ...

  2. QTreeView 动态设置复选框

    目录 动态设置复选框的步骤 QT MVC的概念 模型 添加数据 获取数据 视图 槽函数 动态设置复选框的步骤 初始化一个QTreeView 的界面,并设置相关的model // 在ui中新建两个QTr ...

  3. 超多树形结构的JavaScript菜单实例

    超多树形结构的JavaScript菜单实例,树控菜单,有大家常用的展开折叠型,也就是节点树:还有的是多级的菜单,类似树型,右键菜单树,还有的是悬浮层树,都比较不错,希望大家喜欢. http://www ...

  4. 雷林鹏分享:jQuery EasyUI 树形菜单 - 创建带复选框的树形菜单

    jQuery EasyUI 树形菜单 - 创建带复选框的树形菜单 easyui 的树(Tree)插件允许您创建一个复选框树.如果您点击一个节点的复选框,这个点击的节点信息将向上和向下继承.例如:点击 ...

  5. 数据库树形结构、多级联动的表设计

    问题:二级联动.多级联动等树形结构的数据,如何设计表格. 场景:省市县三级联动.商品的分类等. 参考:https://www.zhihu.com/question/20417447 最常用的一种方法是 ...

  6. 关于JFace带复选框的树

    树的复选框用CheckboxTreeViewer实现.由于其子类ContainerCheckedTreeViewer在没有选择全部子节点时可以自动将父节点设置成灰选,所以实现树的复选框更多的是用Con ...

  7. Ztree树的复选框和获取选择的节点实例和代码

    0 zTree简介 树形控件的使用是应用开发过程中必不可少的.zTree 是一个依靠 jQuery 实现的多功能 "树插件".优异的性能.灵活的配置.多种功能的组合是 zTree ...

  8. 带复选框和简单描述的Qt QTreeWidget树形控件的简单使用

    Qt QTreeWidget树形控件的简单使用 具有选择框的树形控件 具有选择框的树形控件 效果:当选中顶层的树形节点时,子节点全部被选中:当取消选中顶层树形节点时,子节点全部被取消:当选中子节点时, ...

  9. javascript 计算器、动态时钟、表格复选框全选(扩展)、轮播图、36选7、随机数...

    <!doctype html> <html> <head> <meta charset="utf-8"> <title> ...

最新文章

  1. IT兄弟连 JavaWeb教程 Servlet转发
  2. 数字图像处理实验(15):PROJECT 06-02,Pseudo-Color Image Processing
  3. 详解机器视觉照明重点内容
  4. java model1和model2_Java项目(4)——探究两种开发模型的异同-Model1与Model2
  5. mysql row 日志格式_mysql row日志格式下 查看binlog sql语句
  6. Maximum Mode
  7. 长江中游水文站点流量数据下载及处理
  8. 在Virtualbox虚拟机中安装MSDOS(简易教程)
  9. 《彼得·林奇的成功投资》书中的精髓:如何选择帮助我们实现资产翻10倍的股票?以及如何避开让我们血本无归的股票?
  10. 国美易卡利用数据挖掘技术,国美易卡精准分析决策
  11. 分享一个能对java代码进行“tokenize”的python库
  12. gitter 卸载_最佳Gitter频道:数学
  13. 2019年3月最新windwows101809教育版激活密钥及其下载地址
  14. 浅谈上溢overflow和下溢underflow
  15. 如何将spine的素材导入unity
  16. SDUTOJ 2178 - 链表的有序集合
  17. 2019年6月1日第十四周UML项目日志
  18. QQ不能远程控制的解决办法
  19. HTTP POST 请求工具类
  20. 【ARM】迅为rk3568开发板buildroot添加桌面应用

热门文章

  1. semver The semantic versioner for npm
  2. 教你用RNN实现人工智能写作
  3. 游戏接靠谱sdk在靠谱助手模拟器上闪退问题
  4. 简易Alpha(Go) Zero教程(A Simple Alpha(Go) Zero Tutorial)
  5. 微信小程序HTTPS证书部署案例 1
  6. 费曼系列--数学与物理的关系
  7. 解决‘ADB‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件
  8. WS_DAN算法代码研读之wsdan.py(三)
  9. 四国军棋引擎开发(10)局面评估优化
  10. 二进制数转化为十进制数(栈的学习练习)