ios 环境下创建树目录

本文介绍了在iOS应用中,如何根据数据库数据构建动态树目录结构的需求,以及避免使用二维数组的低效方法,转而采用树形结构来存储目录。通过定义`eTreeNode`数据结构和`eTreeMenu`控件,实现了树的动态加载和节点点击刷新。核心代码包括`eTreeMenu.h`和`eTreeMenu.m`,并提供了数据库查询和界面绘制的实现。文章还展示了实现效果,并提供了代码下载链接。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

需求:

最近在项目中遇到一个需求:自动读取数据库中的纪录,并根据type字段分层,构建目录树。要求能够在点击父节点的时候动态刷新子节点,并完成相关操作。

在windows 环境中,自己也做过或者用过不少目录树的控件,但是ios平台没有自带的tree目录控件,ok,自己动手写一个。

思路:

开始自己想到过简单的用几个array来动态保存每个层级的元素,例如根结点为array1,一级节点就存在array2,三级节点放在array3 ......由此类推,n级节点就放在 arrayn+1中,这样做比较简单,实现起来不复杂,但是有几个致命的缺陷:

1、在层级不能确定的前提下,需要用一个动态数组保存每一级的元素集合,也就是需要二维数组,在每一次点击节点的时候,需要至少做一次查询,并对该二维数组进行修改。实现效率较低。

2、在层级确定的前提下,可以将数组个素确定,但是如果层级发生变化,需要修改,比较麻烦。

最后,自己考虑用树来保存目录结构,并根据点击要素所在的层级,动态更新显示子节点,该方法只需要在程序加载的时候查询一次数据库,大大提高以后的点击操作效率。下图是自己简单思考后画的草图。

实现:

1、定义树形结构,作为数据目录,并将它作为界面操作的数据源

2、定义数据结构,包括节点的子节点,节点名称,以及节点度

3、定义目录在界面上的展现形式,自定义控件。

4、数据库查询操作代码。

核心代码说明:

eTreeMenu.h

@protocol eTreeMenuDelegate<NSObject>

//多选情况,title : 选择名称  sel : 是否选择
-(void)ChooseItemClick:(NSString *)title andSel:(BOOL)sel;
//清除已经选择的叶子节点
-(void)clearChoosedLeaves;
@end

@interface eTreeNode : NSObject
{
    eTreeNode  *parentNode;
    NSArray    *childrenNode;
    NSString   *displayName;
    NSInteger   nodeDepth;
}
@property(nonatomic,assign) NSInteger   nodeDepth;
@property(nonatomic,retain)NSString   *displayName;
@property(nonatomic,retain)NSArray    *childrenNode;
@property(nonatomic,retain)eTreeNode  *parentNode;
@end

@interface eTreeMenu : UIScrollView<eChooseItemViewDelegate>
{

    id<eTreeMenuDelegate>  sdelegate;
    NSMutableDictionary  *dataSource;
    eTreeNode   *rootNode;
    NSInteger   depth;
    float  maxWidth;
    float  maxHeight;
    sqliteCommand  *sqlComd;
    NSMutableArray *showNodes;
    eTreeNode   *Root;
    //选中的node深度
    NSInteger   selectedNodeDepth;
}

@property(nonatomic,retain)id<eTreeMenuDelegate>  sdelegate;
@property(nonatomic,retain)eTreeNode   *Root;
@property(nonatomic,retain)NSMutableArray *showNodes;
@property(nonatomic,retain)sqliteCommand  *sqlComd;
@property(nonatomic,assign) NSInteger   depth;
@property(nonatomic,retain)eTreeNode *rootNode;
@property(nonatomic,retain)NSMutableDictionary  *dataSource;
@end

eTreeMenu.m

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self setTreeData];
        //self.backgroundColor=[UIColor lightGrayColor];
        
    }
    return self;
}

- (id)initWithFrame:(CGRect)frame andData:(NSMutableDictionary *)data
{
    self.dataSource=data;
    self = [super initWithFrame:frame];
    if (self) {
        
        self.depth=0;
    }
    return self;
}

//设定数据
-(void)setTreeData
{
    
    [self loadAreaData];
    [self initShowNodeWithRoot];
    [self redrawWithNode:self.Root andSelectNode:nil];
}

//加载数据目录
-(void)loadAreaData
{
    eTreeNode  *root=[self createNodeWithCode:@"900000" andName:@"中国" withDepth:1];
    self.Root=root;
}

//利用递归方法实现各个节点
-(eTreeNode *)createNodeWithCode:(NSString *)code andName:(NSString *)dispName withDepth:(int)curDepth
{
    eTreeNode *node;
    NSString *tableName=@"TAB_AREACODE_NEW";
    NSArray  *cols=[NSArray arrayWithObjects:@"MC",@"CODE",@"PARENT", nil];
    
    //更新树的深度
    self.depth=5;//(self.depth<curDepth)?curDepth:self.depth;
    
    if (self.sqlComd==nil) {
        NSString *dbDir=[[NSHomeDirectory() stringByAppendingPathComponent:@"Documents"] stringByAppendingPathComponent:@"/DB/"];
        self.sqlComd= [[sqliteCommand alloc] initSqlLiteParam:dbDir andFile:ANALYSIS_DB_NAME];
    }
    
    NSArray *array=[self.sqlComd getValuesFromSqlite:tableName andCols:cols andAdopter:[NSString stringWithFormat:@" TYPE =%d AND PARENT=%@",curDepth+1,code]];
    
    if (array==nil||[array count]<=0) {
        
        //没有子节点是叶子
        node=[[eTreeNode alloc] initWithParent:nil andChildren:nil andName:dispName andDepth:curDepth];
        
    }else
    {
        //还有子节点
        NSMutableArray  *childs=[[NSMutableArray alloc] init];
        for (int i=0; i<[array count]; i++) {
            
            NSString *_title=[[array objectAtIndex:i] objectAtIndex:0];
            NSString *_code=[[array objectAtIndex:i] objectAtIndex:1];
            eTreeNode *childNode=[self createNodeWithCode:_code andName:_title withDepth:curDepth+1];
            [childs addObject:childNode];
        }
        node=[[eTreeNode alloc] initWithParent:nil andChildren:childs andName:dispName andDepth:curDepth];
    }
    return  node;
}

//绘制部分代码
//由根节点,生成树,created by eric 20140115
-(void)initShowNodeWithRoot
{
    //重新申明
    showNodes=[[NSMutableArray alloc] initWithCapacity:self.depth];
    
    if (self.Root!=nil) {
        
        eTreeNode  *node=self.Root;
        
        for (int i=0; i<self.depth; i++) {
            
            [showNodes addObject:node];
            if (node!=nil && node.childrenNode!=nil) {
                
                //子目录不为空,默认选择第一个
                node=[node.childrenNode objectAtIndex:0];
                
            }else
            {
                node=nil;
            }
        }
    }
}

//重新绘制,根据node节点为主,并选择节点 selNode
-(void)redrawWithNode:(eTreeNode *)parentNode andSelectNode:(eTreeNode *)selNode
{
    for (id view in self.subviews) {
        [view removeFromSuperview];
    }
    
    eTreeNode  *tempNode=(selNode==nil)?parentNode:selNode;
    if (showNodes!=nil) {
        //之前的层级保持不变
        for (int i=0; i<tempNode.nodeDepth-1; i++) {
            
            eTreeNode  *nd=[showNodes objectAtIndex:i];
            [self addLeaves:nd andDepth:i andSelect:[showNodes objectAtIndex:i+1]];
        }
        //当前层级发生变化
        for (int i=tempNode.nodeDepth-1; i<self.depth-1; i++) {
            
            [self addLeaves:tempNode andDepth:i andSelect:tempNode];
            [showNodes setObject:tempNode atIndexedSubscript:i];
            
            if (tempNode!=nil && tempNode.childrenNode!=nil) {
                
                //子目录不为空,默认选择第一个
                tempNode=[tempNode.childrenNode objectAtIndex:0];
            }else
            {
                tempNode=nil;
            }
        }
    }
}

//添加叶子节点,node:节点要素  depth:节点深度 ,并选择节点selNode
-(void)addLeaves:(eTreeNode *)node andDepth:(NSInteger )nodeDepth andSelect:(eTreeNode *)selNode
{
    
    NSLog(@"draw the depth:%d",nodeDepth);
    if (nodeDepth>=0) {
        
        if (node.childrenNode!=nil) {
            
            float  butWidth=140.0;
            float  butHeight=32.0;
            float  offsetX=0.0;
            float  offsetY=0.0;
            
            //maxHeight=(maxHeight>nodeDepth * butHeight)?maxHeight:nodeDepth * butHeight;
            //maxWidth=(maxWidth>[node.childrenNode count]*butWidth)?maxWidth:[node.childrenNode count]*butWidth;
            
            for (int i=0; i<[node.childrenNode count]; i++) {
                
                eTreeNode *item=[node.childrenNode objectAtIndex:i];
                eChooseItemView  *nodeItem=[[eChooseItemView alloc] initWithFrame:CGRectMake(offsetX+(butWidth+4)*i, offsetY+(butHeight+5)*nodeDepth, butWidth, butHeight) andTile:item.displayName];
                NSLog(@".....added the item:%@",item.displayName);
                nodeItem.nodeData=item;
                nodeItem.parentNode=node;
                [self addSubview:nodeItem];
                
                if (item.nodeDepth==selNode.nodeDepth&&[item.displayName isEqualToString:selNode.displayName]) {
                    
                    [nodeItem changeMode];
                }
                
                nodeItem.delegate=self;
            }
            //设定视图大小
            self.contentSize=CGSizeMake(([node.childrenNode count]+1)*butWidth, nodeDepth * butHeight);
        }
    }
}


//添加叶子节点,node:节点要素  depth:节点深度
-(void)addLeaves:(eTreeNode *)node andDepth:(NSInteger )nodeDepth
{
    if (nodeDepth>=0) {
        
        if (node.childrenNode!=nil) {
            
            float  butWidth=140.0;
            float  butHeight=32.0;
            float  offsetX=0.0;
            float  offsetY=0.0;
            
            //maxHeight=(maxHeight>nodeDepth * butHeight)?maxHeight:nodeDepth * butHeight;
            //maxWidth=(maxWidth>[node.childrenNode count]*butWidth)?maxWidth:[node.childrenNode count]*butWidth;
            
            for (int i=0; i<[node.childrenNode count]; i++) {
            
                eTreeNode *item=[node.childrenNode objectAtIndex:i];
                eChooseItemView  *nodeItem=[[eChooseItemView alloc] initWithFrame:CGRectMake(offsetX+(butWidth+4)*i, offsetY+(butHeight+5)*nodeDepth, butWidth, butHeight) andTile:item.displayName];
                NSLog(@".....added the item:%@",item.displayName);
                nodeItem.nodeData=item;
                nodeItem.parentNode=node;
                [self addSubview:nodeItem];
                nodeItem.delegate=self;
            }
            //设定视图大小
            self.contentSize=CGSizeMake(([node.childrenNode count]+1)*butWidth, nodeDepth * butHeight);
        }
    }
}

-(void)ChooseItemClick:(UIButton *)sender andSel:(BOOL)sel andTitle:(NSString *)title
{
    eChooseItemView  *send=(eChooseItemView *)sender;
    
    if (send.nodeData.nodeDepth != self.depth )
    {
        [self redrawWithNode:send.parentNode andSelectNode:send.nodeData];
        [self redrawWithNode:send.parentNode andSelectNode:send.nodeData];
    }
    //如果选中节点是之前节点的子节点,清除之前选中项目
    if (selectedNodeDepth<send.nodeData.nodeDepth) {
        
        [self.sdelegate clearChoosedLeaves];
    }
    selectedNodeDepth=send.nodeData.nodeDepth;
    [self.sdelegate ChooseItemClick:title andSel:sel];
}
-(void)dealloc
{
    [dataSource release];
    [rootNode release];
    [sqlComd release];
    [showNodes release];
    [Root release];
    [super dealloc];

}
@end


展示效果:

补充:

1、相关代码已经上传到csdn,欢迎各位下载。下载地址: http://download.youkuaiyun.com/detail/xwz19861215311x/6858163

2、由于时间关系,代码没有做优化,存在一些bug,欢迎各位批评指正。(QQ:435011569)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值