IOS 树形结构
===
树形控件在多列列表、多级菜单中使用比较常见,比如:国家-省份-城市 多级选择、学校-专业-班级 多级选择等等。然而IOS自带控件中并不存在树形控件,我们要在IOS开发中使用树形控件,通常需要自己扩展UITableView列表控件。
现在在这里开源一个自己写的高扩展性,高复用性的IOS树形结构控件。
支持无限极树形结构。
使用的是非递归方式。
代码简单易懂,扩展方便。
图片演示如下:
parentId : 该节点的父控件id号,如果为-1则表示该节点为根节点
nodeId : 每个节点自身的id号,是每个节点的唯一标示
name : 节点的名称
depth : 该节点所带的树形结构中的深度,根节点的深度为0
expand : 该节点是否处于展开状态
```
/**
* 每个节点类型
*/
@interface Node : NSObject
@property (nonatomic , assign) int parentId;//父节点的id,如果为-1表示该节点为根节点
@property (nonatomic , assign) int nodeId;//本节点的id
@property (nonatomic , strong) NSString *name;//本节点的名称
@property (nonatomic , assign) int depth;//该节点的深度
@property (nonatomic , assign) BOOL expand;//该节点是否处于展开状态
/** *快速实例化该对象模型 */ - (instancetype)initWithParentId : (int)parentId nodeId : (int)nodeId name : (NSString *)name depth : (int)depth expand : (BOOL)expand;
@end ```
//----------------------------------中国的省地市关系图3,2,1-------------------------------------------- Node *country1 = [[Node alloc] initWithParentId:-1 nodeId:0 name:@"中国" depth:0 expand:YES]; Node *province1 = [[Node alloc] initWithParentId:0 nodeId:1 name:@"江苏" depth:1 expand:NO]; Node *city1 = [[Node alloc] initWithParentId:1 nodeId:2 name:@"南通" depth:2 expand:NO]; Node *city2 = [[Node alloc] initWithParentId:1 nodeId:3 name:@"南京" depth:2 expand:NO]; Node *city3 = [[Node alloc] initWithParentId:1 nodeId:4 name:@"苏州" depth:2 expand:NO]; Node *province2 = [[Node alloc] initWithParentId:0 nodeId:5 name:@"广东" depth:1 expand:NO]; Node *city4 = [[Node alloc] initWithParentId:5 nodeId:6 name:@"深圳" depth:2 expand:NO]; Node *city5 = [[Node alloc] initWithParentId:5 nodeId:7 name:@"广州" depth:2 expand:NO]; Node *province3 = [[Node alloc] initWithParentId:0 nodeId:8 name:@"浙江" depth:1 expand:NO]; Node *city6 = [[Node alloc] initWithParentId:8 nodeId:9 name:@"杭州" depth:2 expand:NO]; //----------------------------------美国的省地市关系图0,1,2-------------------------------------------- Node *country2 = [[Node alloc] initWithParentId:-1 nodeId:10 name:@"美国" depth:0 expand:YES]; Node *province4 = [[Node alloc] initWithParentId:10 nodeId:11 name:@"纽约州" depth:1 expand:NO]; Node *province5 = [[Node alloc] initWithParentId:10 nodeId:12 name:@"德州" depth:1 expand:NO]; Node *city7 = [[Node alloc] initWithParentId:12 nodeId:13 name:@"休斯顿" depth:2 expand:NO]; Node *province6 = [[Node alloc] initWithParentId:10 nodeId:14 name:@"加州" depth:1 expand:NO]; Node *city8 = [[Node alloc] initWithParentId:14 nodeId:15 name:@"洛杉矶" depth:2 expand:NO]; Node *city9 = [[Node alloc] initWithParentId:14 nodeId:16 name:@"旧金山" depth:2 expand:NO];//----------------------------------日本的省地市关系图0,1,2-------------------------------------------- Node *country3 = [[Node alloc] initWithParentId:-1 nodeId:17 name:@"日本" depth:0 expand:YES]; NSArray *data = [NSArray arrayWithObjects:country1,province1,city1,city2,city3,province2,city4,city5,province3,city6,country2,province4,province5,city7,province6,city8,city9,country3, nil];
TreeTableView *tableview = [[TreeTableView alloc] initWithFrame:CGRectMake(0, 20, CGRectGetWidth(self.view.frame), CGRectGetHeight(self.view.frame)-20) withData:data]; [self.view addSubview:tableview];
通过简单以上三步,你就可以把该树形控件集成到你的项目中。
树形结构的列表用的其实就是UITableView控件,但是如何能够让UItableView能够动态的增加和删除指定的行数的cell是实现树形结构的关键所在。 这时候我们需要用到两个UItableView自带的行数:
- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation; - (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;第一个函数用来在指定的位置插入cells,第二个函数用来在指定的位置删除cells,并且这二个函数都自带多种动画效果,让删除和插入的过程不至于太突兀、有种渐变的感觉,具有良好的用户体验。 对于这几个动画做了尝试: UITableViewRowAnimationFade : 渐变效果 UITableViewRowAnimationRight : 右边进入,右边消失 UITableViewRowAnimationLeft : 左边进入,左边消失 UITableViewRowAnimationTop : 顶部进入,顶部消失 UITableViewRowAnimationBottom : 顶部进入,底部消失
在调用insertRowsAtIndexPaths和deleteRowsAtIndexPaths的时候一定要先改变数据源,在调用上述函数,不然会产生crash。
接下来把TreeTableView的主要代码展示出来,因为本来代码量就不大,而且代码中注释也比较全,希望能够帮助大家理解。
#import "TreeTableView.h" #import "Node.h"@interface TreeTableView ()
@property (nonatomic , strong) NSArray *data;//传递过来已经组织好的数据(全量数据)
@property (nonatomic , strong) NSMutableArray *tempData;//用于存储数据源(部分数据)
@end
@implementation TreeTableView
-(instancetype)initWithFrame:(CGRect)frame withData : (NSArray *)data{ self = [super initWithFrame:frame style:UITableViewStyleGrouped]; if (self) { self.dataSource = self; self.delegate = self; _data = data; _tempData = [self createTempData:data]; } return self; }
/**
-(NSMutableArray *)createTempData : (NSArray *)data{ NSMutableArray *tempArray = [NSMutableArray array]; for (int i=0; i<data.count i node objectatindex:i if addobject:node return temparray mark uitableviewdatasource required numberofrowsinsection: _tempdata.count cellforrowatindexpath: static nsstring uitableviewcell dequeuereusablecellwithidentifier:node_cell_id cell="[[UITableViewCell" alloc initwithstyle:uitableviewcellstyledefault reuseidentifier:node_cell_id objectatindex:indexpath.row nsmutablestring string for appendstring: appendstring:node.name cell.textlabel.text="name;" optional heightforheaderinsection: heightforrowatindexpath: heightforfooterinsection: uitableviewdelegate didselectrowatindexpath: nsuinteger startposition="indexPath.row+1;" endposition="startPosition;" bool expand="NO;" parentnode.nodeid node.expand="!node.expand;" insertobject:node atindex:endposition removeallnodesatparentnode:parentnode break nsmutablearray array nsindexpath indexpathforrow:i insection:0 addobject:tempindexpath insertrowsatindexpaths:indexpatharray withrowanimation:uitableviewrowanimationnone deleterowsatindexpaths:indexpatharray parentnode : indexofobject:parentnode parentnode.depth>startPosition) { [_tempData removeObjectsInRange:NSMakeRange(startPosition+1, endPosition-startPosition-1)]; } return endPosition; } </data.count>
在演示项目中,每个cell我都使用系统自带的cell,样式比较简单,如果你要展现更加漂亮的样式,可以自定义cell。
同时,你也可以扩展该数据模型,运动到更加复杂的业务处理中。比如以下场景:
github下载地址:https://github.com/yixiangboy/TreeTableView
如果觉得对你还有些用,给一颗star吧。你的支持是我继续的动力。
个人博客地址:http://blog.csdn.net/yixiangboy
以前看过很多别人的博客,学到不少东西。现在准备自己也开始谢谢博客,希望能够帮到一些人。如果工作不是特别忙的话,准备每周写一篇。
我的联系方式:新浪微博