列表!阶层(树状)显示 NSOutlineView
NSOutlineView 是 NSTableView 的子类别,但它具有阶层(树状)显示的功能,可以将子项目展开或关闭,然而NSTableView实现方式使用阵列来表示,NSOutlineView具有子项目的关系,会使用自订类型的物件来储存整个结构的资料当做DataSource,另外以下文章会以阶层来描述具有树状、阶层结构的说明。
自订结构资料来源
具有阶层的结构来说,一个结构分为上层与下层,当下层中有资料时,下层的角色即为转为上层,然而资料则为下层以此类推,所以就能先完成一个基础结构:
这里以一间公司的组成来说明,一间公司内可能会有很多部门,部门中有主管,主管又分为一级主管,一级主管下有二级主管,二级主管下有它所带领的同仁,整 个组织结构下来可以先订定一个结构资料物件名为Staff:
//-----------start----------- // // Staff.h // NSOutlineViewDemo // // Created by danny on 2015/3/3. // Copyright (c) 2015年 danny. All rights reserved. // #import <Foundation/Foundation.h> @interface Staff : NSObject + (id)staffName:(NSString*)name; - (id)initWithName:(NSString*)name; - (BOOL)isBoss; - (NSString*)name; - (void)addStaff:(Staff*)staff; - (NSArray*) getStaffs; - (NSInteger) getStaffNumbers; @end //------------end------------
以员工为主要物件,员工也能是老板(isBoss),而老板下也会有员工(addStaff:),将几个功能实作为method:
//-----------start----------- // // Staff.m // NSOutlineViewDemo // // Created by danny on 2015/3/3. // Copyright (c) 2015年 danny. All rights reserved. // #import "Staff.h" @implementation Staff { NSMutableArray *_staffInStaff; NSString *_name; } - (id)init { return [self initWithName:@"name"]; } + (id)staffName:(NSString*)name { return [[Staff alloc] initWithName:name]; } //设定员工名字 - (id)initWithName:(NSString*)name { self = [super init]; if (self) { // Initialize self. _staffInStaff = [NSMutableArray array]; _name = [name copy]; } return self; } - (NSString*)name { return _name; } //是否为老板,此员工下还有挂员工即为老板 - (BOOL)isBoss { return (_staffInStaff.count>0?YES:NO); } - (void)addStaff:(Staff*)staff { [_staffInStaff addObject:staff]; } - (NSInteger)getStaffNumbers { return [_staffInStaff count]; } - (NSArray*)getStaffs { return [_staffInStaff copy]; } - (NSString*)description { return _name; } @end //------------end------------
配置NSOutlineView UI
增加NSOutlineView
将NSOutlineView拖至IB:
因我们只使用一个Table Column
,所以删除Table Column
使之保持1个Table Column
:
已经删除结果如下:
删除Table Cell View
此次并无要客制化,所以先将Table Cell View
删除,保留Text Cell
让阶层显示资料时只显示其字串内容:
如果你有使用Table Cell View
客制化显示内容时,就可以实际像如下:
实作上比较覆杂,这往后有机会再介绍,上图片结果为开发者Perspx实作好的PXSourceList的Demo,有兴趣可以下载回来试试。
物件与UI进行连结
以上准备工作完成后,接下来要将UI与物件及程式整个做连结完成一个Demo。
NSOutlineView连结
程式中加上IBOutlet准备与UI做连结:
@interface AppDelegate () : : @property (weak) IBOutlet NSOutlineView *outlineV; : : @end
进行与UI的连结:
选择刚建立好的outlineV
IBOutlet:
连结完成如图下:
写程式产生内容
首先利用建立好的Staff
物件进行建立员工资料:
//-----------start----------- Staff *dept1 = [Staff staffName:@"王董"]; [dept1 addStaff:[Staff staffName:@"小王"]]; [dept1 addStaff:[Staff staffName:@"杨吉"]]; //------------end------------
目前有个部门主管为王董
,它带领了小王
、杨吉
,所以先建立主管,因它也是员工,所以以Staff
物件建立,之后因它带领2位同仁,所以直接将同仁加入王董Staff
物件中,此时王董的角色为部门主管也是公司员工,这里结构来说是以员工
主,所以显示的都是以名字为阶层,并非部门,上面例子可以知道王董
下有小王
、杨吉
。
之后再将整个部门结构加入公司,公司以阵列的型态以便能加上很多部门,先将dept1加入:
//-----------start----------- company = [NSMutableArray array]; Staff *dept1 = [Staff staffName:@"王董"]; [dept1 addStaff:[Staff staffName:@"小王"]]; [dept1 addStaff:[Staff staffName:@"杨吉"]]; [company addObject:dept1]; //------------end------------
依照此模式范例要加入三个部门,其中二个部门下有同仁,其一个部门只有主管自已一个人:
//-----------start----------- company = [NSMutableArray array]; Staff *dept1 = [Staff staffName:@"王董"]; [dept1 addStaff:[Staff staffName:@"小王"]]; [dept1 addStaff:[Staff staffName:@"杨吉"]]; [company addObject:dept1]; Staff *dept2 = [Staff staffName:@"张市"]; [dept2 addStaff:[Staff staffName:@"林林"]]; [dept2 addStaff:[Staff staffName:@"陈林"]]; [company addObject:dept2]; Staff *dept3 = [Staff staffName:@"枸本"]; [company addObject:dept3]; //------------end------------
加入后呈现结果如下图结构:
不过加入结构后,此时要以此结构当做DataSource,让NSOutletView使用时能取得结构上的资料:
//-----------start----------- //.m _outlineV.dataSource = self; _outlineV.delegate = self; //------------end------------
.h要增加DataSource、Delegate:
//-----------start----------- @interface AppDelegate : NSObject <NSApplicationDelegate, NSOutlineViewDataSource,NSOutlineViewDelegate> //------------end------------
将NSOutletView指定为self,并在该物件下实作DataSource需要的内容:
//-----------start----------- - (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item { if (!item) { return [company count];//无item内容为第一层,所以显示第一层的Staff数量 } return [(Staff*)item getStaffNumbers];//非第一层时会将目前这层的物件传入,此时我们列出这层下的Staff数量 } //显示index阵列值中的内容 - (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item { if (!item) { return [company objectAtIndex:index];//无item内容为第一层,所以显示第一层的内容 } return [[(Staff*)item getStaffs] objectAtIndex:index];//非第一层时会将目前这层的物件传入,此时我们列出这层下是否有还有 } //返回YES代表下层还有物件要列出 - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item { if (!item) { return NO;//无item内容时代表已经无下层物件 } return [(Staff*)item isBoss];//非第一层时会将目前这层的物件传入,此时我们列出这层下还有Staff时会将isBoss=YES } /* NOTE: this method is optional for the View Based OutlineView. */ - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item { NSLog(@"%@",item); //提供Staff的名字 return [(Staff*)item name]; } //------------end------------
程式内容:
//-----------start----------- // // AppDelegate.h // NSOutlineViewDemo // // Created by danny on 2015/3/2. // Copyright (c) 2015年 danny. All rights reserved. // #import <Cocoa/Cocoa.h> @interface AppDelegate : NSObject <NSApplicationDelegate, NSOutlineViewDataSource,NSOutlineViewDelegate> @end //------------end------------
//-----------start----------- // // AppDelegate.m // NSOutlineViewDemo // // Created by danny on 2015/3/2. // Copyright (c) 2015年 danny. All rights reserved. // #import "AppDelegate.h" #import "Staff.h" @interface AppDelegate () @property (weak) IBOutlet NSWindow *window; @property (weak) IBOutlet NSOutlineView *outlineV; @end @implementation AppDelegate { NSMutableArray *company; } - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { // Insert code here to initialize your application company = [NSMutableArray array]; Staff *dept1 = [Staff staffName:@"王董"]; [dept1 addStaff:[Staff staffName:@"小王"]]; [dept1 addStaff:[Staff staffName:@"杨吉"]]; [company addObject:dept1]; Staff *dept2 = [Staff staffName:@"张市"]; [dept2 addStaff:[Staff staffName:@"林林"]]; [dept2 addStaff:[Staff staffName:@"陈林"]]; [company addObject:dept2]; Staff *dept3 = [Staff staffName:@"枸本"]; [company addObject:dept3]; _outlineV.dataSource = self; _outlineV.delegate = self; } - (void)applicationWillTerminate:(NSNotification *)aNotification { // Insert code here to tear down your application } - (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item { if (!item) { return [company count];//无item内容为第一层,所以显示第一层的Staff数量 } return [(Staff*)item getStaffNumbers];//非第一层时会将目前这层的物件传入,此时我们列出这层下的Staff数量 } //显示index阵列值中的内容 - (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item { if (!item) { return [company objectAtIndex:index];//无item内容为第一层,所以显示第一层的内容 } return [[(Staff*)item getStaffs] objectAtIndex:index];//非第一层时会将目前这层的物件传入,此时我们列出这层下是否有还有 } //返回YES代表下层还有物件要列出 - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item { if (!item) { return NO;//无item内容时代表已经无下层物件 } return [(Staff*)item isBoss];//非第一层时会将目前这层的物件传入,此时我们列出这层下还有Staff时会将isBoss=YES } /* NOTE: this method is optional for the View Based OutlineView. */ - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item { NSLog(@"%@",item); //提供Staff的名字 return [(Staff*)item name]; } @end //------------end------------
结论
NSOutlineView的使用与NSTableView方式很相近,都是需要提供DataSource、Delegate的方法,可以先参考列出正在执行的应用程式文章中的篇例程式来理解一下NSTableView的使用方式再接着重新看此篇文章会比较容易理解。