iOS 树形结构菜单(参照以前大神写的博客)
来源:互联网 发布:国际原油行情分析软件 编辑:程序博客网 时间:2024/06/05 23:07
说明:写的东西是参照某位大神写的博客,但是我自己的数据他有些没有,然后我就根据大神的博客重新写了下,希望对大家有用
1.数据的形式,请求下来的数据将会是如下格式
@property (nonatomic, strong) NSString *itemParentId;//父节点的id@property (nonatomic, strong) NSString *itemId;//本节点的id@property (nonatomic, strong) NSString *itemName;//本节点的名称@property (nonatomic, assign) BOOL expand;//该节点是否处于展开状态这些数据中有父节点的ID和自身的ID,还有自身的名字,并且自己写一个属性expand,之后来判断该节点是否处于展开或者收起状态
2.那么根据这个节点我们创建一个Model,代码如下,代码中我写好了注释
创建一个Node类继承于NSObject
Node.h中的代码:
#import <Foundation/Foundation.h>@interface Node : NSObject//这里是数据@property (nonatomic, strong) NSString *itemParentId;//父节点的id@property (nonatomic, strong) NSString *itemId;//本节点的id@property (nonatomic, strong) NSString *itemName;//本节点的名称@property (nonatomic, strong) NSString *itemIndex;//本节点在该级菜单中的索引值(这个值可以不用关注,这是我自己用到的数据)@property (nonatomic, assign) BOOL expand;//该节点是否处于展开状态@property (nonatomic, strong) NSString *siteSSCID;//(这个值也不用关注,也是我自己的数据的东西)/** * 快速实例化该对象模型 * * @param itemParentId 父节点的id * @param itemId 本节点的id * @param itemName 本节点的名称 * @param itemIndex 本节点在该级菜单中的索引值 * @param expand 该节点是否处于展开状态 * * @return 一个node实例 */- (instancetype)initWithParentId:(NSString *)itemParentId nodeId:(NSString *)itemId name:(NSString *)itemName index:(NSString *)itemIndex siteSSCID:(NSString *)itemSiteSSCID expand:(BOOL)expand;@end
#import "Node.h"@implementation Node- (instancetype)initWithParentId:(NSString *)itemParentId nodeId:(NSString *)itemId name:(NSString *)itemName index:(NSString *)itemIndex siteSSCID:(NSString *)itemSiteSSCID expand:(BOOL)expand{ self = [self init]; if (self) { self.itemParentId = itemParentId; self.itemId = itemId; self.itemName = itemName; self.itemIndex = itemIndex; self.siteSSCID = itemSiteSSCID; self.expand = expand; } return self;}@end
这样做完之后,我们就把数据的Model构建完成了,现在根据这个Model来创建树型菜单
3.创建一个TreeTableView类继承于UITableView,其中的注释在代码中,我就直接粘贴代码了
TreeTableView.h中的代码
#import <UIKit/UIKit.h>@interface TreeTableView : UITableView@property (nonatomic , strong) NSArray *dataS;//传递过来已经组织好的数据(全量数据)@property (nonatomic , strong) NSMutableArray *tempData;//用于存储数据源(部分数据,这个数据是根据dataS来最初显示在界面上的菜单的名字)@property (nonatomic , strong) void (^selectBlock)(NSString *);//这个block是我用来传当点击到最后子节点的时候,具体的传值我还没写
-(NSMutableArray *)createTempData : (NSArray *)data;//初始化表格数据(这个方法是根据总数据先初始化最开显示的最上层的菜单,然后得到tempData)@end
TreeTableView.m中的代码
#import "TreeTableView.h"#import "Node.h"@interface TreeTableView ()<UITableViewDataSource,UITableViewDelegate>{ NSMutableDictionary *_dic;//处理重复数据用 NSInteger _depth;//深度,就是是第几级菜单}@end@implementation TreeTableView-(instancetype)initWithFrame:(CGRect)frame{ self = [super initWithFrame:frame style:UITableViewStyleGrouped]; if (self) { _dic = [@{} mutableCopy]; self.dataSource = self; self.delegate = self; } return self;}/** * 初始化数据源 */-(NSMutableArray *)createTempData : (NSArray *)data{ NSMutableArray *tempArray = [@[] mutableCopy]; NSMutableArray *keys = [@[] mutableCopy]; for (int i=0; i<data.count; i++) { Node *node = [self.dataS objectAtIndex:i]; if ([node.itemParentId isEqualToString:@"0"]) { node.expand = YES; [_dic setObject:node forKey:node.itemName]; [keys addObject:node.itemName]; } } //处理初始化数据中重复(这里处理的是我自己数据中带有重复的数据) [keys enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { Node *node1 = tempArray.lastObject; Node *node2 = _dic[obj]; if (node1==nil || ![node1.itemName isEqualToString:node2.itemName]){ [tempArray addObject:_dic[obj]]; } }]; return tempArray;}#pragma mark *** UITableViewDelegate/UITableViewDataSource ***-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
//首先展示的数据是tempData的数据
return self.tempData.count;}- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ static NSString *NODE_CELL_ID = @"node_cell_id"; //定义cell UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:NODE_CELL_ID]; if (!cell) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:NODE_CELL_ID]; }
Node *node = [self.tempData objectAtIndex:indexPath.row];//取到数据中node的name _depth = 0;//全局变量,深度置为0 NSInteger tem = [self findDepthOfNode:node];//找到某个node在整个树形菜单的深度
</pre><pre code_snippet_id="1897900" snippet_file_name="blog_20160924_11_7136027" name="code" class="objc">//处理界面展示的层次关系,在前面加空格实现,也可以自定义cell,在前面加上imageView来更形象的展示,我就没有具体写了 NSMutableString *name = [NSMutableString string]; for (int i=0; i<tem; i++) { [name appendString:@" "]; } [name appendString:node.itemName];
cell.textLabel.font = [UIFont systemFontOfSize:15.0];//设置cell中字体的大小 cell.textLabel.text = name;//设置cell显示的内容 cell.backgroundColor = [UIColor brownColor];//设置cell的背景颜色 return cell;}//这个方法是用来找它的深度的。根据某个节点找到节点在整个菜单中深度(作用就是会在后面收起的时候做判断)- (NSInteger)findDepthOfNode:(Node *)node{ Node * nextNode;
//根据传过来的node判断是不是是根节点(我设置的根节点,就是没有父节点的节点的父节点ID为“0”) if (![node.itemParentId isEqualToString:@"0"]) {
//如果不是父节点,那么它的深度加一 _depth ++;
//然后去循环找视图展示的数据中,这个节点的父节点 for (int i = 0; i < self.tempData.count; i++) { nextNode = [self.tempData objectAtIndex:i];
//判断如果找到有这样一个父节点数据在tempData中那么就break if ([node.itemParentId isEqualToString:nextNode.itemId]) { break; } }
//用递归的思想循环找 [self findDepthOfNode:nextNode]; }
//最终获取到数据在tempData中的深度(也就是在第几层) return _depth;}- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{ return 0.01;}- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ return 40;}- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section{ return 0.01;}- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ //这里是关键,就是点击展开或者收起的代码 BOOL expand = NO; NSUInteger startPosition = indexPath.row+1;//点击某一行,获取开始的位置 NSUInteger endPosition = startPosition;//结束的位置 Node *parentNode = [_tempData objectAtIndex:indexPath.row];//获取到某一行的节点
//这里可以先不用管,我还没有写具体的内容,这里是判断如果点击之后再无子节点,那么它的层级最低,可以将点击的东西传值到其他页面 if (self.selectBlock) { self.selectBlock(parentNode.itemName); }
//在总的数据dataS中查找
for (int i=0; i<self.dataS.count; i++) { Node *node = [self.dataS objectAtIndex:i]; Node *node2 = [_tempData objectAtIndex:endPosition-1]; //判断语句后面一个判断是来做数据重复使用的,但是在数据中 还有CONN_ID来区别同样的数据,可能以后需要这些重复的数据在界面上显示,那么久直接删除后面一个判断就可以
//判断语句前一个语句是查找所有数据,一个个查看它的父节点是不是点击的那个,是的话就插入数据到表格的相应位置,否则删除表格中相应的数据 if ([node.itemParentId isEqualToString:parentNode.itemId] && ![node2.itemId isEqualToString:node.itemId]) {
node.expand = !node.expand; if (node.expand) { [_tempData insertObject:node atIndex:endPosition]; expand = YES; }else{ expand = NO; endPosition = [self removeAllNodesAtParentNode:parentNode]; break; } endPosition++; } } //获得需要修正的indexPath NSMutableArray *indexPathArray = [NSMutableArray array]; for (NSUInteger i=startPosition; i<endPosition; i++) { NSIndexPath *tempIndexPath = [NSIndexPath indexPathForRow:i inSection:0]; [indexPathArray addObject:tempIndexPath]; } //插入或者删除相关节点 if (expand) { [self insertRowsAtIndexPaths:indexPathArray withRowAnimation:UITableViewRowAnimationNone]; }else{ [self deleteRowsAtIndexPaths:indexPathArray withRowAnimation:UITableViewRowAnimationNone]; }}/** * 删除该父节点下的所有子节点(包括孙子节点) * * @param parentNode 父节点 * * @return 邻接父节点的位置距离该父节点的长度,也就是该父节点下面所有的子孙节点的数量 */-(NSUInteger)removeAllNodesAtParentNode : (Node *)parentNode{
NSUInteger startPosition = [_tempData indexOfObject:parentNode]; NSUInteger endPosition = startPosition + 1; _depth =0; NSInteger y = [self findDepthOfNode:parentNode]; for (NSUInteger i=startPosition+1; i<self.tempData.count; i++) { Node *node = [_tempData objectAtIndex:i]; _depth = 0; NSInteger x = [self findDepthOfNode:node]; // 判断节点深度是否大于等于父节点深度 if ( x <= y) { break; } endPosition++; node.expand = NO; } if (endPosition>startPosition) { [self.tempData removeObjectsInRange:NSMakeRange(startPosition+1, endPosition-startPosition-1)]; } return endPosition;}@end
4.这两个类创建完成,在ViewController中的初始化并调用
ViewController.h代码
#import <UIKit/UIKit.h>@interface ViewController : UIViewController@endViewController.m代码
#import "ViewController.h"#import "TreeTableView.h"#import "Node.h"@interface ViewController ()@property (nonatomic,strong)TreeTableView *treeTableView;@end@implementation ViewController- (void)viewDidLoad { [super viewDidLoad]; //初始化数据 self.treeTableView.dataS = [self LoadDataForTableView]; self.treeTableView.tempData = [self.treeTableView createTempData:self.treeTableView.dataS]; //加载视图 [self.view addSubview:self.treeTableView];}#pragma mark *** Private Mathod ***- (NSArray *)LoadDataForTableView{ // 构造数据 // 构造总的数据 //我这里是以“0”作为最根节点,如果是其他作为根节点,要在代码中做相应的修改 Node *node = [[Node alloc]initWithParentId:@"0" nodeId:@"国家1" name:@"中国" index:@"这个数据是我自己的数据你要不要无所谓" siteSSCID:@"这个数据也是" expand:NO]; Node *node1 = [[Node alloc]initWithParentId:@"0" nodeId:@"国家2" name:@"俄罗斯" index:@"这个数据是我自己的数据你要不要无所谓" siteSSCID:@"这个数据也是" expand:NO]; //这里的ParentId一定是父节点的nodeId Node *node2 = [[Node alloc]initWithParentId:@"国家1" nodeId:@"省份1" name:@"四川" index:@"这个数据是我自己的数据你要不要无所谓" siteSSCID:@"这个数据也是" expand:NO]; Node *node3 = [[Node alloc]initWithParentId:@"国家1" nodeId:@"省份2" name:@"浙江" index:@"这个数据是我自己的数据你要不要无所谓" siteSSCID:@"这个数据也是" expand:NO]; Node *node4 = [[Node alloc]initWithParentId:@"国家1" nodeId:@"省份3" name:@"江苏" index:@"这个数据是我自己的数据你要不要无所谓" siteSSCID:@"这个数据也是" expand:NO]; Node *node5 = [[Node alloc]initWithParentId:@"省份1" nodeId:@"省份1城市1" name:@"成都" index:@"这个数据是我自己的数据你要不要无所谓" siteSSCID:@"这个数据也是" expand:NO]; Node *node6 = [[Node alloc]initWithParentId:@"省份1" nodeId:@"省份1城市2" name:@"巴中" index:@"这个数据是我自己的数据你要不要无所谓" siteSSCID:@"这个数据也是" expand:NO]; Node *node7 = [[Node alloc]initWithParentId:@"省份1" nodeId:@"省份1城市3" name:@"内江" index:@"这个数据是我自己的数据你要不要无所谓" siteSSCID:@"这个数据也是" expand:NO]; Node *node8 = [[Node alloc]initWithParentId:@"省份2" nodeId:@"省份2城市1" name:@"温州" index:@"这个数据是我自己的数据你要不要无所谓" siteSSCID:@"这个数据也是" expand:NO]; Node *node9 = [[Node alloc]initWithParentId:@"省份3" nodeId:@"省份3城市1" name:@"常州" index:@"这个数据是我自己的数据你要不要无所谓" siteSSCID:@"这个数据也是" expand:NO]; Node *node10 = [[Node alloc]initWithParentId:@"国家2" nodeId:@"国家2省份1" name:@"莫斯科" index:@"这个数据是我自己的数据你要不要无所谓" siteSSCID:@"这个数据也是" expand:NO]; Node *node11 = [[Node alloc]initWithParentId:@"国家2" nodeId:@"国家2省份2" name:@"除了莫斯科,我也不知道有那个城市" index:@"这个数据是我自己的数据你要不要无所谓" siteSSCID:@"这个数据也是" expand:NO]; //造个四级菜单数据 Node *node12 = [[Node alloc]initWithParentId:@"省份1城市1" nodeId:@"省份1城市1区域1" name:@"青羊区" index:@"这个数据是我自己的数据你要不要无所谓" siteSSCID:@"这个数据也是" expand:NO]; Node *node13 = [[Node alloc]initWithParentId:@"省份1城市1" nodeId:@"省份1城市1区域2" name:@"高新区" index:@"这个数据是我自己的数据你要不要无所谓" siteSSCID:@"这个数据也是" expand:NO]; Node *node14 = [[Node alloc]initWithParentId:@"省份1城市1" nodeId:@"省份1城市1区域3" name:@"武侯区" index:@"这个数据是我自己的数据你要不要无所谓" siteSSCID:@"这个数据也是" expand:NO]; NSArray *arry = @[node,node1,node2,node3,node4,node5,node6,node7,node8,node9,node10,node11,node12,node13,node14]; return arry;}#pragma mark *** Lazy Loading ***- (TreeTableView *)treeTableView{ if (!_treeTableView) { _treeTableView = [[TreeTableView alloc]initWithFrame:CGRectMake(0, 20, self.view.bounds.size.width, self.view.bounds.size.height)]; } return _treeTableView;}
0 0
- iOS 树形结构菜单(参照以前大神写的博客)
- iOS开发大神的博客
- 靠,以前没有写博客的习惯。。
- 以前在新浪写的博客
- 序:大神都是写博客的
- 旅居日本的ios 大神的博客
- iOS大神牛人的博客集合
- 树形结构菜单选中的菜单高亮实现
- iOS大神博客(持续更新中。。。)
- iOS大神博客汇总
- 使用RecyclerView写树形结构的TreeRecyclerView
- 从网易博客转过来几篇以前写的文章
- 我第一天写博客,把以前的程序放上去(用Java写的迷宫)
- 利用多叉树实现Ext JS中的无限级树形菜单(一种构建多级有序树形结构JSON的方法)
- 以前的博客
- 以前的博客
- 自己写的简单大方的树形导航菜单
- easyui树形结构读取菜单
- [算法导论]2.1节编程练习C++实现
- 【C++心路历程2】我做不出来二分法 这个题就超时了 好气哦
- Spring Security——基于表达式的权限控制
- 大数据学习(二):Hadoop源码分析
- 自定义过渡
- iOS 树形结构菜单(参照以前大神写的博客)
- Django中文文档-The Django Book
- sql if 用法
- 如何利用jquery获取下拉框中所选的内容以及select里value的使用
- Android SDKVersion 参数列表
- Notes: Stand alone daemons and Super daemon
- 无bug注释
- Java 8 Lambda函数编程入门(二)
- 【codevs 1227】方格取数2(费用流)