分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.youkuaiyun.com/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、 findNodeByKey 和getNodes 方法:
+(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.youkuaiyun.com/jiangjunshow
