使用UITableView实现树视图

分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow

也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

               

 

 

本文的目的,是研究有没有可能在一个TableView中呈现树形数据,尤其是树形菜单。众多的网络资料都强调,Cocoa框架不支持树形视图,苹果推荐程序员使用TableViewController+NavigationController的方式展现树形菜单。如果2-3层的树形数据还可以忍受,万一层次稍多一点,必须反复的用导航按钮在视图中转来转去,显然并不太方便。何况笔者认为2-3层的导航也要切换多次视图,也是一种浪费。

 

一、搭建基本框架 1

二、实现树节点 2

三、实现树 3

四、实现TreeViewCell 4

五、 在TreeViewController中展现树视图 6

六、 一些改进 8

七、 进一步的封装 10

一、搭建基本框架

 

1、新建Winddow-Based-Application项目TreeView;

2、删除MainWindow.xib,删除plist中Main nib file basename;

3、修改main.m: int retVal = UIApplicationMain(argc,argv, nil, @"TreeViewAppDelegate");

 

4、修改TreeViewAppDelegate,删除属性window的声明,删除window的synthesize语句。增加变量声明:TreeViewController* rootViewController;

修改(BOOL)application:didFinishLaunchingWithOptions方法 :

    window=[[UIWindow alloc]initWithFrame:CGRectMake(0, 0, 320, 480)];

rootViewController=[[TreeViewController alloc]init];

[window addSubview:rootViewController.view];

   [window makeKeyAndVisible];

return YES;

5、新建类TreeViewController,继承UIViewController.

二、实现树节点

 

1、树由节点构成。树节点是一种链表结构。它包含有父节点、子节点等内容,同时应实现节点添加等操作。

2、新建TreeNode类。

===============.h文件==============

#import <Foundation/Foundation.h>

@interface TreeNode : NSObject {

TreeNode* p_node;//父节点

NSMutableArray* children;//子节点

id data;//节点可以包含任意数据

NSString* title;//节点要显示的文字

NSString* key;//主键,在树中唯一

BOOL expanded;//标志:节点是否已展开,保留给TreeViewCell使用的

}

@property (retain) TreeNode*p_node;

@property (retain) id data;

@property (retain) NSString*title,*key;

@property (assign) BOOL expanded;

@property (retain)NSMutableArray* children;

-(int) deep;//hasChildren的访问方法

-(BOOL)hasChildren;

//子节点的添加方法

-(void)addChild:(TreeNode*)child;

-(int)childrenCount;

@end

===============.m文件==============

#import "TreeNode.h"

 

@implementation TreeNode

@synthesize p_node,children,data,title,key,expanded;

-(id)init{

if(self=[super init]) {

p_node=nil;

children=nil;

key=nil;

}

return self;

}

-(void)addChild:(TreeNode*)child{

if(children==nil) {

children=[[NSMutableArray alloc]init];

}

child.p_node=self;

[children addObject:child];

}

-(int)childrenCount{

return children==nil?0:children.count;

}

-(int)deep{

return p_node==nil?0:[p_node.deep]+1;

}

-(BOOL)hasChildren{

if(children==nil || children.count==0)

return NO;

else return YES;

}

@end

 

三、实现树

 

1、节点其实就是一种树,有父节点、子节点。但树的最大用处在于遍历树、查找任意子节点。我们可以在TreeNode中增加遍历树的操作。

2、在TreeNode的头文件中增加方法声明:

+(TreeNode*)findNodeByKey:(NSString*)_key:(TreeNode*)node;

+(void)getNodes:(TreeNode*)root:(NSMutableArray*) array;

两个方法都使用递归对树节点进行遍历,不同的是前者在查找到key相同的节点返回,而后者则直接把树的所有节点添加到数组中返回。

4、 findNodeByKeygetNodes 方法:

+(TreeNode*)findNodeByKey:(NSString*)_key:(TreeNode*)node{

if([_key isEqualToString:[node key]]) {//如果node就匹配,返回node

return node;

}else if([node hasChildren]){//如果node有子节点,查找node 的子节点

for(TreeNode* each in [node children]){

NSLog(@"retrievenode:%@ %@",each.title,each.key);

TreeNode* a=[TreeNodefindNodeByKey:_key :each];

if (a!=nil) {

return a;

}

}

}

//如果node没有子节点,则查找终止,返回nil

returnnil;

}

+(void)getNodes:(TreeNode*)root:(NSMutableArray*) array{

[array addObject:root];

if([root hasChildren]) {

for(TreeNode* each in [root children]){

[TreeNode getNodes:each :array];

}

}

return;

}

四、实现TreeViewCell

 

1、新建类TreeViewCell.

2、TreeViewCell.h文件:

#import <UIKit/UIKit.h>

#import "TreeNode.h"

 

@interface TreeViewCell : UITableViewCell {

UIButton* btnExpand;//按钮:用于展开子节点

SEL onExpand;//selector:点击“+”展开按钮时触发

TreeNode* treeNode;//每个单元格表示一个节点

UILabel* label;//标签:显示节点title

id owner;//表示 onExpand方法委托给哪个对象

UIImageView* imgIcon;//图标

}

@property (assign) SEL onExpand;

@property (retain) id owner;

@property (retain)UIImageView* imgIcon;

-(void)setTreeNode:(TreeNode *)node;

@end

3、TreeViewCell.m文件:

#import "TreeViewCell.h"

 

 

@implementation TreeViewCell

@synthesize onExpand,imgIcon,owner;

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString*)reuseIdentifier {

   if ((self = [super initWithStyle:style reuseIdentifier:reuseIdentifier])) {

        // Initialization code

   }

   return self;

}

 

 

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {

 

   [super setSelected:selected animated:animated];

 

   // Configure the view forthe selected state

}

-(void)onExpand:(id)sender{

if([treeNode hasChildren]) {//如果有子节点

//NSLog(@"%d",[treeNodehasChildren]);

treeNode.expanded=!treeNode.expanded;//切换展开/收起状态

if(treeNode.expanded){//若展开状态设置“+/-”号图标

[btnExpand setImage:[UIImage imageNamed:

 @"minus.png"] forState:UIControlStateNormal];

}else {

[btnExpand setImage:[UIImage imageNamed:

 @"plus.png"] forState:UIControlStateNormal];

}

if(owner!=nil && onExpand!=nil)//若用户设置了onExpand属性则调用

[owner performSelector:onExpand withObject:treeNode];

}

}

-(void)setTreeNode:(TreeNode *)node{

treeNode=node;

if (label==nil) {

//NSLog(@"labelis nil");

imgIcon=[[UIImageView alloc]initWithFrame:

  CGRectMake(20+(15*node.deep), 6, 32, 32)];

label=[[UILabel alloc]initWithFrame:

  CGRectMake(50+(15*node.deep), 0, 200,36)];

btnExpand=[[UIButton alloc]initWithFrame:CGRectMake((15*node.deep), 5, 32, 32)];

[btnExpand addTarget:self action:@selector(onExpand:)

forControlEvents:UIControlEventTouchUpInside];

[imgIcon setImage:[UIImage imageNamed:@"folder_small.png"]];

[self addSubview:label];

[self addSubview:imgIcon];

[self addSubview:btnExpand];

}else {

[label setFrame:CGRectMake(50+(15*node.deep), 0, 200, 36)];

[imgIcon setFrame:CGRectMake(20+(15*node.deep), 6, 32, 32)];

[btnExpand setFrame:CGRectMake(15*node.deep, 5, 32, 32)];

}

if ([node hasChildren]) {

NSLog(@"nodehas children");

if ([node expanded]) {

[btnExpand setImage:[UIImage imageNamed:@"minus.png"]

   forState:UIControlStateNormal];

}else {

UIImage *img=[UIImage imageNamed:@"plus.png"];

//NSLog(@"%d",img==nil);

[btnExpand setImage:img

   forState:UIControlStateNormal];

}

}else {

[btnExpand setImage:nil forState:UIControlStateNormal];

}

[label setText:node.title];

}

- (void)dealloc {

    [super dealloc];

}

 

 

@end

 

五、 在TreeViewController中展现树视图

 

1、接下来应该建立一个TableViewController,使用我们的TreeViewCell。新建类TreeViewController。

2、TreeViewController.h文件:

#import <Foundation/Foundation.h>

#import "TreeNode.h"

 

@interface TreeViewController : UITableViewController

<UITableViewDelegate,UITableViewDataSource>

{

TreeNode* tree;

NSMutableArray* nodes;

}

@end

3、TreeViewController.m文件:

#import "TreeViewController.h"

#import "TreeViewCell.h"

 

@implementation TreeViewController

-(void)viewDidLoad{

[super viewDidLoad];

tree=[[TreeNode alloc]init];

tree.deep=0;

tree.title=@"根节点";

TreeNode* node[10];

for (int i=0; i<10; i++) {

node[i]=[[TreeNode alloc]init];

node[i].title=[NSString stringWithFormat:@"节点%d",i];

node[i].key=[NSString stringWithFormat:@"%d",i];

}

    [node[0] addChild:node[1]];

[node[0] addChild:node[2]];

[node[0] addChild:node[3]];

[node[2] addChild:node[4]];

[node[2] addChild:node[5]];

[node[2] addChild:node[6]];

[node[6] addChild:node[7]];

[node[6] addChild:node[8]];

[node[3] addChild:node[9]];

[tree addChild:node[0]];

nodes=[[NSMutableArray alloc]init];

[TreeNode getNodes:tree :nodes];

}

#pragmamark ===table view datasource methods====

-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{

return 1;

}

-(NSInteger)tableView:(UITableView *)tablenumberOfRowsInSection:(NSInteger)section{

return nodes.count;

}

-(UITableViewCell*)tableView:(UITableView *)tableViewcellForRowAtIndexPath:(NSIndexPath*)indexPath{

static NSString* cellid=@"cell";

TreeViewCell* cell=(TreeViewCell*)[tableView dequeueReusableCellWithIdentifier:

  cellid];

if (cell==nil) {

cell=[[[TreeViewCell alloc]initWithStyle:UITableViewCellStyleDefault

 reuseIdentifier:cellid]autorelease];

}

TreeNode* node=[nodes objectAtIndex:indexPath.row];

[cell setOwner:self];

[cell setOnExpand:@selector(onExpand:)];

[cell setTreeNode:node];

return cell;

}

-(void)onExpand:(TreeNode*)node{

nodes=[[NSMutableArray alloc]init];

[TreeNode getNodes:tree :nodes];

[self.tableView reloadData];

}

@end

首先,继承TableViewController并实现UITableViewDelegate和UITableViewDataSource协议。

viewDidLoad方法中,我们使用TreeNode构建了一棵树,并把树的根节点和所有需要展开的节点放到nodes数组中(请看TreeNode的getNodes方法是怎么定义的)。注意,因为一开始所有节点的expanded总是false(不展开),所以node数组中除了根节点外,没有其他元素。

tableView的数据源方法没有什么特别的。但对于TreeView,我们还需要实现一个方法(这里是onExpand方法,但其实叫什么名字无所谓),然后对所有cell使用setOnExpand把这个方法的selector传递给TreeViewCell,在TreeViewCell中,这个方法会在展开(+号)按钮点击时触发。

-(void)onExpand:(TreeNode*)node方法有一点特殊,它带了一个参数。由于在TreeViewCell中,触发该方法时用到了  performSelector:withObject:方式而不是普通的performSelect:发送,所以TreeViewCell有可能把这个单元格所包含的TreeNode对象传递到TreeViewController的onExpand:来。从而可以通过这个参数读到各个单元格的modal数据。

六、 一些改进>

 

1.无论我们需不需要,TreeView上总是会显示一个“根节点”,哪怕这个根节点并没有什么实际的用途。

如果我们可以控制节点是否需要显示就好了。要实现这一点,需要在TreeViewCell中增加一个新的变量:

BOOL hidden;//标志,节点是否隐藏

然后修改getNodes方法,将 [array addObject:root];修改为:

if(![roothidden])//只有节点被设置为不隐藏的时候才返回节点

[array addObject:root];

最后,把TreeViewController的loadView方法稍作修改,使根节点隐藏但同时展开:

tree.hidden=YES;

tree.expanded=YES;

 

这样,根节点不显示了,显示的是它已被展开的子节点“节点0” 。

2、节点左边的文件夹图标真是另人讨厌,我们可以把它替换成自己的图片吗?只需要在tableView: cellForRowAtIndexPath:方法中修改TreeViewcell的image属性。

NSString* filename=[NSString

stringWithFormat:@"%d.png",[node.key intValue]+1];

UIImage* img=[UIImage imageNamed:filename];

[cell.imgIcon setImage:img];

 

但记住这些操作必须在[cell setTreeNode:node];语句之后,因为setTreeNode方法会将节点的image属性设为默认的文件夹图片,在此之前修改显然是没有用的:

图片似乎了大一点,把它们从(40*40)调整为默认的32*32 就好。

3.最后还有一个问题,上一级和

给我老师的人工智能教程打call!http://blog.csdn.net/jiangjunshow
这里写图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值