一、引言
和iOS开发中的UITableView有很大差别,NSTableView并非是一个可滚动的列表视图,其是一个不可滚动、支持多列多行的原始列表视图。若要使NSTableView支持滚动,通常会将其嵌套入NSScrollView控件中。与UITableView类似,NSTableView的数据也是用过DataSource代理来提供,通过Delegate代理来进行表格视图的定制化。在OS X v10.6版本之前,NSTableView中行数据载体视图必须是NSCell的子类,之后版本的OS X支持开发者创建基于View的TableView视图,同样也支持基于Cell的TabelView视图,在开发者,我们可以根据实际需求选择。
二、构建一个简单的列表视图
首先新建一个测试工程,在ViewController.m文件中编写如下代码:
#import "ViewController.h"
@interface ViewController()<NSTableViewDelegate,NSTableViewDataSource>
@end
@implementation ViewController
{
NSTableView * _tableView;
NSMutableArray * _dataArray;
}
- (void)viewDidLoad {
[super viewDidLoad];
_dataArray = [NSMutableArray array];
for (int i=0; i<20; i++) {
[_dataArray addObject:[NSString stringWithFormat:@"%d行数据",i]];
}
NSScrollView * scrollView = [[NSScrollView alloc] init];
scrollView.hasVerticalScroller = YES;
scrollView.frame = self.view.bounds;
[self.view addSubview:scrollView];
_tableView = [[NSTableView alloc]initWithFrame:self.view.bounds];
NSTableColumn * column = [[NSTableColumn alloc]initWithIdentifier:@"test"];
[_tableView addTableColumn:column];
_tableView.delegate = self;
_tableView.dataSource = self;
[_tableView reloadData];
scrollView.contentView.documentView = _tableView;
}
-(NSInteger)numberOfRowsInTableView:(NSTableView *)tableView{
return _dataArray.count;
}
-(id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row{
return _dataArray[row];
}
@end
运行工程效果如下图:
这是一个最简单的TableView示例,但是细读代码,麻雀虽小五脏俱全。首先NSTableView中的列是由NSTableColumn类描述的。一个列表可以有多个列。也正如前面所说,numberOfRowsInTableView方法为数据源代理必须实现的方法,其中需要返回列表的行数。objectValueForTableColumn方法则是基于Cell的TableView必须实现的方法,其中需要返回每个列表行所填充的数据。
三、关于NSTableColume的探究
NSTableColume简单理解就是一列,其中可以进行此列样式的相关设置,NSTableColumn类中常用属性解析如下:
//初始化方法,指定一个列ID
- (instancetype)initWithIdentifier:(NSString *)identifier;
//与此列关联的ID
@property (copy) NSString *identifier;
//关联的TableView
@property (nullable, assign) NSTableView *tableView;
//设置列宽度
@property CGFloat width;
//设置最小列宽度
@property CGFloat minWidth;
//设置最大列宽度
@property CGFloat maxWidth;
//设置类标题
@property (copy) NSString *title;
/*
列标题视图 开发者可以对其进行修改
需要注意,NSTableHeaderCell是继承自NSTextFieldCell
*/
@property (strong) __kindof NSTableHeaderCell *headerCell;
//设置此列是否可以进行编辑
@property (getter=isEditable) BOOL editable;
//进行列尺寸的调整 以列标题视图的宽度为标准
- (void)sizeToFit;
//提供了这个属性,会在列标题那里显示一个排序按钮 点击列标题后可以进行排序操作(会回调相关协议方法)
@property (nullable, copy) NSSortDescriptor *sortDescriptorPrototype;
//设置列尺寸的调整模式 枚举如下
/*
typedef NS_OPTIONS(NSUInteger, NSTableColumnResizingOptions) {
NSTableColumnNoResizing = 0, //不允许进行宽度调整
//详见NSTabelView的columnAutoresizingStyle属性
NSTableColumnAutoresizingMask = ( 1 << 0 ), //使用tableView的column调整策略
NSTableColumnUserResizingMask = ( 1 << 1 ), //允许用户进行尺寸调整
};
*/
@property NSTableColumnResizingOptions resizingMask;
//设置列头的提示标题 当鼠标悬停在类标题上时 会显示此提示
@property (nullable, copy) NSString *headerToolTip;
//设置此列是否隐藏
@property (getter=isHidden) BOOL hidden;
//设置此列所有行的数据载体视图 如果不设置 默认为NSTextFieldCell
@property (strong) id dataCell;
//为TableView列表提供数据载体视图
- (id)dataCellForRow:(NSInteger)row;
四、Cell-Base:基于Cell的TableView视图
Cell-Base是OS X早起版本中常用的构造TabelView的方式,其中每一行的数据载体都必须是NSCell的子类。如本文开头的示例代码,Cell-Base的TableView必须实现的两个协议方法是numberOfRowsInTableView和objectValueForTableColumn方法,第一个方法设置列表行数,第2个方法设置每个数据载体对应的具体数据。需要注意,如果只实现这两个方法,则NSTableView会自动从列对象NSTableColume中取具体的行视图,通过dataCellForRow方法。当objectValueForTableColumn方法将每个行具体的数据返回后,会调用cell的setObjectValue方法(因此如果要自定义cell,必须实现这个方法)。如果我们要对Cell的渲染进行一些定制,可以在如下方法中实现:
//将要渲染cell调用的方法 开发者可以拿到cell对象做定制
- (void)tableView:(NSTableView *)tableView willDisplayCell:(id)cell forTableColumn:(nullable NSTableColumn *)tableColumn row:(NSInteger)row;
实现下面的方法可以返回一个自定义的Cell,如果实现了这个方法,则TableView不会再从NSTableColumn对象中拿Cell实例:
//返回自定义的Cell实例
/*
需要注意,这个方法在第一次调用的时候 tableColumu对象是nil 如果这时返回了Cell,则此Cell宽度会覆盖整个列表
在使用时要多加注意
*/
- (nullable NSCell *)tableView:(NSTableView *)tableView dataCellForTableColumn:(nullable NSTableColumn *)tableColumn row:(NSInteger)row;
其他方法的实例代码如下:
#import "ViewController.h"
#import "MyCell.h"
@interface ViewController()<NSTableViewDelegate,NSTableViewDataSource>
@end
@implementation ViewController
{
NSTableView * _tableView;
NSMutableArray * _dataArray;
}
- (void)viewDidLoad {
[super viewDidLoad];
_dataArray = [NSMutableArray array];
for (int i=0; i<20; i++) {
[_dataArray addObject:[NSString stringWithFormat:@"%d行数据",i]];
}
NSScrollView * scrollView = [[NSScrollView alloc] init];
scrollView.hasVerticalScroller = YES;
scrollView.frame = self.view.bounds;
[self.view addSubview:scrollView];
_tableView = [[NSTableView alloc]initWithFrame:self.view.bounds];
NSTableColumn * column = [[NSTableColumn alloc]initWithIdentifier:@"test"];
NSTableColumn * column2 = [[NSTableColumn alloc]initWithIdentifier:@"test2"];
column2.width = 100;
column2.minWidth = 100;
column2.maxWidth = 100;
column2.title = @"数据";
column2.editable = YES ;
column2.headerToolTip = @"提示";
column2.hidden=NO;
column2.sortDescriptorPrototype = [NSSortDescriptor sortDescriptorWithKey:@"title" ascending:NO];
column.resizingMask =NSTableColumnUserResizingMask;
// column.dataCell = [[NSButtonCell alloc]initTextCell:@""];
[_tableView addTableColumn:column];
[_tableView addTableColumn:column2];
_tableView.delegate = self;
_tableView.dataSource = self;
scrollView.contentView.documentView = _tableView;
}
//设置行数 通用
-(NSInteger)numberOfRowsInTableView:(NSTableView *)tableView{
return _dataArray.count;
}
//绑定数据
-(id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row{
return _dataArray[row];
}
//用户编辑列表
- (void)tableView:(NSTableView *)tableView setObjectValue:(nullable id)object forTableColumn:(nullable NSTableColumn *)tableColumn row:(NSInteger)row{
NSLog(@"%@",object);
_dataArray[row] = object;
}
//cell-base的cell展示前调用 可以进行自定制
- (void)tableView:(NSTableView *)tableView willDisplayCell:(id)cell forTableColumn:(nullable NSTableColumn *)tableColumn row:(NSInteger)row{
NSTextFieldCell * _cell = cell;
_cell.textColor = [NSColor redColor];
}
//设置是否可以进行编辑
- (BOOL)tableView:(NSTableView *)tableView shouldEditTableColumn:(nullable NSTableColumn *)tableColumn row:(NSInteger)row{
return YES;
}
//设置鼠标悬停在cell上显示的提示文本
- (NSString *)tableView:(NSTableView *)tableView toolTipForCell:(NSCell *)cell rect:(NSRectPointer)rect tableColumn:(nullable NSTableColumn *)tableColumn row:(NSInteger)row mouseLocation:(NSPoint)mouseLocation{
return @"tip";
}
//当列表长度无法展示完整某行数据时 当鼠标悬停在此行上 是否扩展显示
- (BOOL)tableView:(NSTableView *)tableView shouldShowCellExpansionForTableColumn:(nullable NSTableColumn *)tableColumn row:(NSInteger)row{
return YES;
}
//设置cell的交互能力
/*
如果返回YES,则Cell的交互能力会变强,例如NSButtonCell的点击将会调用- (void)tableView:(NSTableView *)tableView setObjectValue方法
*/
- (BOOL)tableView:(NSTableView *)tableView shouldTrackCell:(NSCell *)cell forTableColumn:(nullable NSTableColumn *)tableColumn row:(NSInteger)row{
return YES;
}
//自定义cell
- (nullable NSCell *)tableView:(NSTableView *)tableView dataCellForTableColumn:(nullable NSTableColumn *)tableColumn row:(NSInteger)row{
if (tableColumn!=nil) {
MyCell * cell = [[MyCell alloc]init];
return cell;
}
return nil;
}
-(CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row{
return 30;
}
//排序回调函数
-(void)tableView:(NSTableView *)tableView sortDescriptorsDidChange:(NSArray<NSSortDescriptor *> *)oldDescriptors{
NSLog(@"%@",oldDescriptors[0]);
}
@end