# NSTableView
[TOC]
# 简单上手
首先,我们上手做一个如下的简单tableView,然后对部分细节、属性进行详解。
## 1、 创建基本的tableView
效果如下:
![NSTableView_Simple](/Users/MelissaShu/macos/NSTableView_Simple.png)
1.1 设置tableView 为成员变量,并添加协议
```
@interface SecWindowController ()<NSTableViewDelegate,NSTableViewDataSource>
@property (nonatomic,strong) NSTableView *tableView;
@end
```
1.2 懒加载初始化tableView并添加相关设置
```
#pragma mark - Getter
- (NSTableView *)tableView
{
if(!_tableView){
_tableView = [[NSTableView alloc] initWithFrame:CGRectMake(10, 10, 200, 200)];
}
return _tableView;
}
- (void)windowDidLoad {
[super windowDidLoad];
[self tableViewSetting];
}
- (void)tableViewSetting{
self.window.backgroundColor = [NSColor cyanColor];
//第一列
NSTableColumn *column1 = [[NSTableColumn alloc] initWithIdentifier:@"columnFrist"];
column1.title = @"columnFrist";
[column1 setWidth:100];
[self.tableView addTableColumn:column1];
//第二列
NSTableColumn * column2 = [[NSTableColumn alloc] initWithIdentifier:@"columnSecond"];
column2.title = @"columnSecond"; //如果为空,则默认显示‘Field’
[column2 setWidth:70];
[self.tableView addTableColumn:column2];
//第三列
NSTableColumn * column3 = [[NSTableColumn alloc] initWithIdentifier:@"column3"];
column3.title = @"column3";
[column3 setWidth:80];
[self.tableView addTableColumn:column3];
self.tableView.focusRingType = NSFocusRingTypeNone;//tableview获得焦点时的风格
self.tableView.selectionHighlightStyle = NSTableViewSelectionHighlightStyleRegular;//行高亮的风格
self.tableView.backgroundColor = [NSColor orangeColor];
self.tableView.usesAlternatingRowBackgroundColors = YES; //背景颜色的交替,一行白色,一行灰色。设置后,原来设置的 backgroundColor 就无效了。
self.tableView.delegate = self;
self.tableView.dataSource = self;
self.tableView.gridColor = [NSColor magentaColor];
//实现tableview的滚动效果
NSScrollView *tableContainerView = [[NSScrollView alloc] initWithFrame:CGRectMake(5, 5, 300, 300)];
tableContainerView.backgroundColor = [NSColor redColor];
[tableContainerView setDocumentView:self.tableView];
[tableContainerView setDrawsBackground:NO];//不画背景(背景默认画成白色)
[tableContainerView setHasVerticalScroller:YES];//有垂直滚动条
//[_tableContainer setHasHorizontalScroller:YES]; //有水平滚动条
tableContainerView.autohidesScrollers = YES;//自动隐藏滚动条(滚动的时候出现)
[self.window.contentView addSubview:tableContainerView];
}
```
1.3 实现数据源、代理、通知方法
```
#pragma mark - NSTableViewDelegate,NSTableViewDataSource
//返回行数
-(NSInteger)numberOfRowsInTableView:(NSTableView *)tableView{
return 15;
}
-(NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row{
//获取表格列的标识符
NSString *columnID = tableColumn.identifier;
NSLog(@"columnID : %@ ,row : %d",columnID,row);
NSString *strIdt = @"123";
NSTableCellView *cell = [tableView makeViewWithIdentifier:strIdt owner:self];
if (!cell) {
cell = [[NSTableCellView alloc]init];
cell.identifier = strIdt;
}
cell.wantsLayer = YES;
cell.layer.backgroundColor = [NSColor yellowColor].CGColor;
cell.imageView.image = [NSImage imageNamed:@"swift"];
cell.textField.stringValue = [NSString stringWithFormat:@"cell %ld",(long)row];
return cell;
}
#pragma mark - 行高
-(CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row{
return 44;
}
#pragma mark - 是否可以选中单元格
-(BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(NSInteger)row{
//设置cell选中高亮颜色
NSTableRowView *myRowView = [self.tableView rowViewAtRow:row makeIfNecessary:NO];
[myRowView setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleRegular];
[myRowView setEmphasized:NO];
NSLog(@"shouldSelect : %d",row);
return YES;
}
//选中的响应
-(void)tableViewSelectionDidChange:(nonnull NSNotification *)notification{
NSTableView* tableView = notification.object;
NSLog(@"didSelect:%@",notification.userInfo);
}
```
***
## 2、一些细节
- tableView 必须添加到 scrollView 中,才能够显示和滑动。
- tableView 的尺寸为 scrollView 的尺寸,非 tableView初始化时的尺寸。
- 设置 usesAlternatingRowBackgroundColors = YES 时,实现背景颜色的交替(如numbers效果)。但是原来设置的 backgroundColor 就无效了。
- 如果没有设置 column 的 title,默认显示为 “Field” 。
- 如果不想显示表头header,设置rect为0即可,如`self.tableView.headerView.frame = NSZeroRect;`
- 如果tableView的x,y值不为0,header显示的内容将会被掩盖一部分。可以尝试。
-
***
# 和iOS的不同点
- Mac OS的tableView需要一个Container来包裹它,才可以滑动,而iOS的不用
- Mac OS的tableview里面多了一个概念叫做column(列),而iOS中没有这个概念
- iOS中有section的概念,可是Mac OS中没有(NSCollectionView中有)
------
# 基本属性 & 常见概念
***
## tableView 视图基本结构
![NSTableView解剖](/Users/MelissaShu/macos/NSTableView解剖.png)
***
## Cell-Base & View-Base
***
### Cell-Base:基于Cell的TableView视图
Cell-Base 是早期常用的方式,其中每一行的数据载体都**必须是NSCell的子类**。但更老、更轻量。
Cell-Base的TableView必须实现的两个数据源方法是`numberOfRowsInTableView` 和 `objectValueForTableColumn` 方法,第一个方法设置列表行数,第2个方法设置每个数据载体对应的具体数据。
如:
```
-(NSInteger)numberOfRowsInTableView:(NSTableView *)tableView{
return 15;
}
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
NSString *rowData = [NSString stringWithFormat:@"%@ - %d",tableColumn.title,row];
return rowData;
}
```
***
### View-Base:基于View的TableView视图
基于View-Base的TableView要比基于Cell的TableView更加灵活,其中每行数据载体可以是**任意NSView的子类**。
***
## NSTableViewColumn
列由 `NSTableViewColumn` 类表示,负责管理列的宽度和行为,例如调整大小和位置。
这个类不是视图,而是**控制器类**。
使用它指定列的行为,但是不能控制列的视觉样式,因为它已经被包含在头部、行和单元格视图中了。
***
## NSTableCellView -
***
## NSTableRowView - 行容器
NSTableRowView用在View-Base的TableView中,其作为**行容器**存在。
在这里设置背景色,和选中的背景色。
***
# 自定义 tableView
如实现 显示文字的tableView
![NSTableView_Custom1](/Users/MelissaShu/macos/NSTableView_Custom1.png)
## 自定义 NSTableCellView
1、创建类 CustomTableCellView 继承自 NSTableCellView,添加 NSString 和 NSTextField 等类型成员变量;
```
@interface CustomTableCellView : NSTableCellView
@property (nonatomic, strong) NSString *string;
@property (nonatomic, strong) NSTextField *label;
@end
```
2、添加控件到cell中,并添加设置方法
```
-(instancetype)initWithFrame:(NSRect)frameRect{
self = [super initWithFrame:frameRect];
if (self) {
[self addSubview:self.label];
}
return self;
}
- (void)setString:(NSString *)string {
self.label.frame = self.bounds;
self.label.stringValue = string;
}
//懒加载设置label
-(NSTextField *)label
{
if (!_label) {
_label = [[NSTextField alloc] initWithFrame:NSZeroRect];
_label.editable = NO;
_label.bordered = YES;
_label.backgroundColor = [NSColor purpleColor];
}
return _label;
}
```
## 自定义 NSTableRowView
1、创建类 CustomTableRowView 继承自 NSTableRowView
2、在.m 文件中添加如下代码:
```
// 自定义 row 被选中的背景色
-(void)drawSelectionInRect:(NSRect)dirtyRect {
if (self.selectionHighlightStyle != NSTableViewSelectionHighlightStyleNone) {
[[NSColor cyanColor] setFill];
NSBezierPath *path = [NSBezierPath bezierPathWithRect:NSInsetRect(self.bounds, 0, 0)];
[path fill];
[path stroke];
}
}
//自定义 row 背景色
- (void)setBackgroundColor:(NSColor *)backgroundColor {
super.backgroundColor = [NSColor yellowColor];
}
```
## 基本属性
```
@property NSTableViewSelectionHighlightStyle selectionHighlightStyle;
//是否强调
@property(getter=isEmphasized) BOOL emphasized;
//设置是否行组风格
@property(getter=isGroupRowStyle) BOOL groupRowStyle;
//是否选中状态
@property(getter=isSelected) BOOL selected;
//其前一行的选中状态
@property(getter=isPreviousRowSelected) BOOL previousRowSelected;
//其后一行的选中状态
@property(getter=isNextRowSelected) BOOL nextRowSelected;
//设置此行是否浮动
@property(getter=isFloating) BOOL floating;
//拖放拖动效果
@property(getter=isTargetForDropOperation) BOOL targetForDropOperation;
//拖放风格
@property NSTableViewDraggingDestinationFeedbackStyle draggingDestinationFeedbackStyle;
//设置拖放目标的缩进量
@property CGFloat indentationForDropOperation;
//背景色
@property(copy) NSColor *backgroundColor;
//子类重写下面方法来进行行容器视图的自定义
//画背景色
- (void)drawBackgroundInRect:(NSRect)dirtyRect;
//画选中背景
- (void)drawSelectionInRect:(NSRect)dirtyRect;
//画分割线
- (void)drawSeparatorInRect:(NSRect)dirtyRect;
//绘制拖放时的用户反馈IU
- (void)drawDraggingDestinationFeedbackInRect:(NSRect)dirtyRect;
//列数
@property(readonly) NSInteger numberOfColumns;
//提供的访问特定视图的方法
- (nullable id)viewAtColumn:(NSInteger)column;
```
***
## NSTableViewDataSource
```
/*
无论基于Cell还是基于View,这个方法都需要实现,用来设置列表的行数
*/
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView;
/*
如果使用cell-base的TableView视图,这个方法是必须实现的,其为要渲染的cell提供数据
*/
- (nullable id)tableView:(NSTableView *)tableView objectValueForTableColumn:(nullable NSTableColumn *)tableColumn row:(NSInteger)row;
/*
这个函数当用户编辑了cell中的内容时会被调用,一般需要在其中进行数据源的修改
*/
- (void)tableView:(NSTableView *)tableView setObjectValue:(nullable id)object forTableColumn:(nullable NSTableColumn *)tableColumn row:(NSInteger)row;
/*
当用户修改了行排序规则时调用的回调
*/
- (void)tableView:(NSTableView *)tableView sortDescriptorsDidChange:(NSArray<NSSortDescriptor *> *)oldDescriptors;
//下面这些方法全部与列表的数据拖拽相关
- (nullable id <NSPasteboardWriting>)tableView:(NSTableView *)tableView pasteboardWriterForRow:(NSInteger)row;
- (void)tableView:(NSTableView *)tableView draggingSession:(NSDraggingSession *)session willBeginAtPoint:(NSPoint)screenPoint forRowIndexes:(NSIndexSet *)rowIndexes NS_AVAILABLE_MAC(10_7);
- (void)tableView:(NSTableView *)tableView draggingSession:(NSDraggingSession *)session endedAtPoint:(NSPoint)screenPoint operation:(NSDragOperation)operation NS_AVAILABLE_MAC(10_7);
- (void)tableView:(NSTableView *)tableView updateDraggingItemsForDrag:(id <NSDraggingInfo>)draggingInfo NS_AVAILABLE_MAC(10_7);
- (BOOL)tableView:(NSTableView *)tableView writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard *)pboard;
- (NSDragOperation)tableView:(NSTableView *)tableView validateDrop:(id <NSDraggingInfo>)info proposedRow:(NSInteger)row proposedDropOperation:(NSTableViewDropOperation)dropOperation;
- (BOOL)tableView:(NSTableView *)tableView acceptDrop:(id <NSDraggingInfo>)info row:(NSInteger)row dropOperation:(NSTableViewDropOperation)dropOperation;
- (NSArray<NSString *> *)tableView:(NSTableView *)tableView namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination forDraggedRowsWithIndexes:(NSIndexSet *)indexSet;
```
***
## NSTableViewDelegate
```
//view-base的TableView相关delegate方法
/*
设置每个数据载体的View
*/
- (nullable NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(nullable NSTableColumn *)tableColumn row:(NSInteger)row;
/*
自定义行视图
*/
- (nullable NSTableRowView *)tableView:(NSTableView *)tableView rowViewForRow:(NSInteger)row NS_AVAILABLE_MAC(10_7);
/*
添加一行时会调用的回调
*/
- (void)tableView:(NSTableView *)tableView didAddRowView:(NSTableRowView *)rowView forRow:(NSInteger)row;
/*
移除一行时会调用的回调
*/
- (void)tableView:(NSTableView *)tableView didRemoveRowView:(NSTableRowView *)rowView forRow:(NSInteger)row;
//cell-base的TableView相关delegate方法
/*
cell将要渲染时调用的回调,可以在其中对cell进行定制
*/
- (void)tableView:(NSTableView *)tableView willDisplayCell:(id)cell forTableColumn:(nullable NSTableColumn *)tableColumn row:(NSInteger)row;
/*
设置某个cell是否可以编辑
*/
- (BOOL)tableView:(NSTableView *)tableView shouldEditTableColumn:(nullable NSTableColumn *)tableColumn row:(NSInteger)row;
/*
设置当鼠标悬停在cell上时 显示的提示文案
*/
- (NSString *)tableView:(NSTableView *)tableView toolTipForCell:(NSCell *)cell rect:(NSRectPointer)rect tableColumn:(nullable NSTableColumn *)tableColumn row:(NSInteger)row mouseLocation:(NSPoint)mouseLocation;
/*
当cell的宽度不够显示完全cell的内容时,设置是否允许鼠标放置扩展cell
*/
- (BOOL)tableView:(NSTableView *)tableView shouldShowCellExpansionForTableColumn:(nullable NSTableColumn *)tableColumn row:(NSInteger)row;
/*
设置是否加强cell的交互能力,这样一些按钮状态的修改也会触发cell编辑的状态
*/
- (BOOL)tableView:(NSTableView *)tableView shouldTrackCell:(NSCell *)cell forTableColumn:(nullable NSTableColumn *)tableColumn row:(NSInteger)row;
/*
设置自定义cell
*/
- (nullable NSCell *)tableView:(NSTableView *)tableView dataCellForTableColumn:(nullable NSTableColumn *)tableColumn row:(NSInteger)row;
//通用的TableView代理方法
/*
设置是否允许修改选中
*/
- (BOOL)selectionShouldChangeInTableView:(NSTableView *)tableView;
/*
设置某行是否可以选中
*/
- (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(NSInteger)row;
/*
当用户通过键盘或鼠标将要选中某行时,返回设置要选中的行
如果实现了这个方法,上面一个方法将不会被调用
*/
- (NSIndexSet *)tableView:(NSTableView *)tableView selectionIndexesForProposedSelection:(NSIndexSet *)proposedSelectionIndexes;
/*
设置某列是否可以被选中
*/
- (BOOL)tableView:(NSTableView *)tableView shouldSelectTableColumn:(nullable NSTableColumn *)tableColumn;
/*
用户点击列头时调用的方法
*/
- (void)tableView:(NSTableView *)tableView mouseDownInHeaderOfTableColumn:(NSTableColumn *)tableColumn;
/*
用法同上
*/
- (void)tableView:(NSTableView *)tableView didClickTableColumn:(NSTableColumn *)tableColumn;
/*
对列进行拖拽改变顺序时调用的方法
*/
- (void)tableView:(NSTableView *)tableView didDragTableColumn:(NSTableColumn *)tableColumn;
/*
设置行高
*/
- (CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row;
/*
下面这些方法与行检索有关
*/
- (nullable NSString *)tableView:(NSTableView *)tableView typeSelectStringForTableColumn:(nullable NSTableColumn *)tableColumn row:(NSInteger)row NS_AVAILABLE_MAC(10_5);
- (NSInteger)tableView:(NSTableView *)tableView nextTypeSelectMatchFromRow:(NSInteger)startRow toRow:(NSInteger)endRow forString:(NSString *)searchString NS_AVAILABLE_MAC(10_5);
- (BOOL)tableView:(NSTableView *)tableView shouldTypeSelectForEvent:(NSEvent *)event withCurrentSearchString:(nullable NSString *)searchString NS_AVAILABLE_MAC(10_5);
/*
设置某行是否绘制成组样式
*/
- (BOOL)tableView:(NSTableView *)tableView isGroupRow:(NSInteger)row;
/*
调整列宽度
*/
- (CGFloat)tableView:(NSTableView *)tableView sizeToFitWidthOfColumn:(NSInteger)column;
/*
设置是否支持列的移动排序
*/
- (BOOL)tableView:(NSTableView *)tableView shouldReorderColumn:(NSInteger)columnIndex toColumn:(NSInteger)newColumnIndex;
//设置某行向左或向右滑动时要显示的功能按钮
/*
typedef NS_ENUM(NSInteger, NSTableRowActionEdge) {
NSTableRowActionEdgeLeading, // 左划
NSTableRowActionEdgeTrailing, // 右划
} NS_ENUM_AVAILABLE_MAC(10_11);
*/
- (NSArray<NSTableViewRowAction *> *)tableView:(NSTableView *)tableView rowActionsForRow:(NSInteger)row edge:(NSTableRowActionEdge)edge NS_AVAILABLE_MAC(10_11);
/*
TableView选中修改时调用
*/
- (void)tableViewSelectionDidChange:(NSNotification *)notification;
/*
TableView列移动完成时调用的函数
*/
- (void)tableViewColumnDidMove:(NSNotification *)notification;
/*
TableView列宽度变化时调用的函数
*/
- (void)tableViewColumnDidResize:(NSNotification *)notification;
/*
TableView选中正在修改时调用的函数
*/
- (void)tableViewSelectionIsChanging:(NSNotification *)notification;
```
***
## 使用示例
### 获取点击事件
点击的交互可以通过通知 `tableViewSelectionDidChange` 获取:
```
- (void)tableViewSelectionDidChange:(NSNotification *)notification {
NSTableView *tableView = notification.object;
NSLog(@"---selection row %ld", tableView.selectedRow);
// CustomTableCellView *contentView = [tableView makeViewWithIdentifier:@"name" owner:self];
CustomTableCellView *contentView = [tableView viewAtColumn:0 row:tableView.selectedRow makeIfNecessary:NO];
contentView.label.wantsLayer = YES;
contentView.label.textColor = [NSColor blueColor];
}
```
## 相关通知
```
//列表选择改变后发的通知
APPKIT_EXTERN NSNotificationName NSTableViewSelectionDidChangeNotification;
//列移动后发的通知
APPKIT_EXTERN NSNotificationName NSTableViewColumnDidMoveNotification;
//列宽度改变后发的通知
APPKIT_EXTERN NSNotificationName NSTableViewColumnDidResizeNotification;
//选择改变时发的通知
APPKIT_EXTERN NSNotificationName NSTableViewSelectionIsChangingNotification;
```
***
# 相关样式枚举
## NSTableColumnResizingOptions - 设置列尺寸的调整模式
@property NSTableColumnResizingOptions resizingMask;
```
typedef NS_OPTIONS(NSUInteger, NSTableColumnResizingOptions) {
NSTableColumnNoResizing = 0, //不允许进行宽度调整
//详见NSTabelView的columnAutoresizingStyle属性
NSTableColumnAutoresizingMask = ( 1 << 0 ), //使用tableView的column调整策略
NSTableColumnUserResizingMask = ( 1 << 1 ), //允许用户进行尺寸调整
};
```
***
## NSTableViewColumnAutoresizingStyle
@property NSTableViewColumnAutoresizingStyle columnAutoresizingStyle;
```
typedef NS_ENUM(NSUInteger, NSTableViewColumnAutoresizingStyle) {
//不可调整
NSTableViewNoColumnAutoresizing = 0,
//平分
NSTableViewUniformColumnAutoresizingStyle,
//从后往前调整
NSTableViewSequentialColumnAutoresizingStyle,
//从前往后调整
NSTableViewReverseSequentialColumnAutoresizingStyle,
//最后一列可调整
NSTableViewLastColumnOnlyAutoresizingStyle,
//第一列可调整
NSTableViewFirstColumnOnlyAutoresizingStyle
};
```
***
## NSTableViewGridLineStyle - 设置分割线风格
```
typedef NS_OPTIONS(NSUInteger, NSTableViewGridLineStyle) {
//无分割线
NSTableViewGridNone = 0,
//竖直分割线
NSTableViewSolidVerticalGridLineMask = 1 << 0,
//水平分割线
NSTableViewSolidHorizontalGridLineMask = 1 << 1,
//水平虚线分割线
NSTableViewDashedHorizontalGridLineMask ,
};
```
***
## NSTableViewRowSizeStyle
@property NSTableViewRowSizeStyle rowSizeStyle;
```
typedef NS_ENUM(NSInteger, NSTableViewRowSizeStyle) {
//默认
NSTableViewRowSizeStyleDefault = -1,
//自定义
NSTableViewRowSizeStyleCustom = 0,
//小尺寸风格
NSTableViewRowSizeStyleSmall = 1,
//中等尺寸风格
NSTableViewRowSizeStyleMedium = 2,
//大尺寸风格
NSTableViewRowSizeStyleLarge = 3,
} NS_ENUM_AVAILABLE_MAC(10_7);
```
***
## NSTableViewSelectionHighlightStyle - 选中的高亮风格
@property NSTableViewSelectionHighlightStyle selectionHighlightStyle;
```
typedef NS_ENUM(NSInteger, NSTableViewSelectionHighlightStyle) {
//无高亮风格
NSTableViewSelectionHighlightStyleNone,
//规则的高亮风格
NSTableViewSelectionHighlightStyleRegular = 0,
//源列表风格
NSTableViewSelectionHighlightStyleSourceList = 1,
```
***
# 参考资料
- 珲少:macOS开发之NSTableView的应用详解
https://my.oschina.net/u/2340880/blog/886861