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

目标:

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. SpringBoot实现万能文件在线预览,已开源,真香!!!
  2. (十五)Java springcloud B2B2C o2o多用户商城 springcloud架构-commonservice-sso服务搭建(一)...
  3. BZOJ3509 [CodeChef] COUNTARI 【分块 + fft】
  4. 优化网站的TDK要注意哪些问题?
  5. 牛X的web报表设计工具Grid++Report
  6. 2020 我的C++的学习之路 第十章 对象和类
  7. 【CQOI2009】叶子的颜色
  8. hosts文件是什么?
  9. 【渝粤题库】广东开放大学 英语语法与基础写作 形成性考核 (2)
  10. react with form
  11. 大数据_Flink_Java版_ProcessFunction(4)_应用案例_高低温分流---Flink工作笔记0069
  12. linux命令行效率,聊聊那些可以提高工作效率的Linux命令
  13. Springcloud实战(五)微服务网关
  14. Python多线程小例子
  15. 【ppt课件制作】Focusky教程 | 如何设置内容全屏显示?
  16. 书籍推荐:国内第一本ASP.NET 3.5 MVC技术专著
  17. 袋鼠云数据中台专栏(五):数栈,企业级一站式数据中台PaaS
  18. 梯度下降-5463. 服务中心的最佳位置
  19. 网联清算平台45家股东名单出炉【附股权明细表】
  20. 开山斧 WEBSHELL管理器 V0.2

热门文章

  1. 国产CAD_CAD制图初学入门:如何用国产CAD软件删除CAD图纸中多余的辅助线?
  2. npm包 semver模块【语义化版本号】
  3. 解决Visual C++ Redistributable for Visual Studio 2015的安装问题
  4. 转】用Maven构建Hadoop项目
  5. 搭建恋爱话术库一个月赚5w真实吗?
  6. 当CPU巨头英特尔盯上GPU:4个月6次出手,从游戏到数据中心市场全面点燃
  7. 2019上半年有哪些营销失败甚至变成危机公关的案例?
  8. 【Vapor】04 Chapter 6:Configuring a Database
  9. 计算机硬盘的分区表结构布局标准,硬盘分区表的结构含义
  10. Linux下面C语言多文件编译