两种方式实现在 Cell Based NSTableView中内嵌复选框
本文以 CellBased NSTableView 为例,讲述如何实现在table view 中内嵌复选框的目的。最终效果如下图所示。
首先创建一个 Cocoa App, 在 MainMenu.xib 中插入一个 NSTableView 控件,并将该table view 改为 Cell Based 类型。目前还没尝试 View Based 实现该目的。后续有时间会继续研究使用 View Based 如何实现。选中我们的tableview 在 Attributes Inspector 栏位设置 Columns 为 3列。并分别设置三个列的 identifier 和 title 为: checked name ID , Checked Name ID。 此时我们的table view 是这样的
下面开始编写我们的代理方法 checkDS.h 和 checkDS.m ,
#import <Foundation/Foundation.h>
#import <Cocoa/Cocoa.h>
@interface checkDS : NSTableView<NSTabViewDelegate, NSTableViewDataSource> {
}
@property (retain) NSTableView *tableView;
@property (assign) NSMutableArray *aryCheck;
@end
因为上面我们的NSTableView 所有列的cell 都是 NSTextFieldCell, 要实现将某一列替换为复选框,总共需要实现如下四个方法,这里直接帖出我的代码,
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView;
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(nullable NSTableColumn *)tableColumn row:(NSInteger)row;
- (NSCell *)tableView:(NSTableView *)tableView dataCellForTableColumn:(nullable NSTableColumn *)tableColumn row:(NSInteger)row
- (void)tableView:(NSTableView *)tableView setObjectValue:(nullable id)object forTableColumn:(nullable NSTableColumn *)tableColumn row:(NSInteger)row
//下面的方法主要设置tableview 显示数据的行数。我们的数据源设为 属性 aryCheck.
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
{
return [aryCheck count];
}
//下面的方法用于设置tableview 显示数据的内容,当列的identifier 为name 和 ID 时,直接从字典中取出对应的值即可,当我们的列为 checked 时,因为要实现复选框的目的,我们进行了一个值的转换,若字典中取出的值为 “yes” 就置为 1, 若字典中取出的值为 "no" 就置为 0, 若有其他情况,这里可以设为其他值,后面我们其他的代理方法会根据这个值(yes 和 no)设置是否为 复选框,根据这里设置的 0 和 1 设置 复选框是选中和不选中状态。
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(nullable NSTableColumn *)tableColumn row:(NSInteger)row
{
NSDictionary *data = [aryCheck objectAtIndex:row];
NSString *identity = [tableColumn identifier];
id obj = nil;
if ([identity isEqualToString:@"checked"]) {
obj = [data valueForKey:@"checked"];
if ([obj isEqualToString:@"yes"]) {
NSNumber *bValue = [NSNumber numberWithBool:YES];
obj = bValue;
} else if ([obj isEqualToString:@"no"]){
NSNumber *bValue = [NSNumber numberWithBool:NO];
obj = bValue;
}
}else if ([identity isEqualToString:@"name"]) {
obj =[data valueForKey:@"name"];
} else if ([identity isEqualToString:@"ID"]) {
obj = [data valueForKey:@"ID"];
}
return obj;
}
现在最基本的两个代理方法都实现了,现在可以先编写我们的主程序查看实现效果了。
主程序主要实现三个目的,一个将我们的控件与属性绑定,而是为我们的tableview提供数据源,三是为我们的控件绑定代理方法。这里基本的操作就省略了,直接上我们的代码吧。
@interface AppDelegate : NSObject <NSApplicationDelegate>
{
IBOutlet NSTableView *tableView;
NSMutableArray *array;
checkDS *cDS;
}
@end
@implementation AppDelegate
- (id)init
{
self = [super init];
if (self) {
cDS = [[checkDS alloc] init];
array = [[NSMutableArray alloc] init];
}
return self;
}
- (void)initTest
{
NSMutableDictionary *dict1 = [NSMutableDictionary dictionaryWithObjectsAndKeys:@"xingxing",@"name", @"1",@"ID", @"no", @"checked", nil];
NSMutableDictionary *dict2 = [NSMutableDictionary dictionaryWithObjectsAndKeys:@"Coco",@"name", @"2",@"ID", @"yes", @"checked", nil];
NSMutableDictionary *dict3 = [NSMutableDictionary dictionaryWithObjectsAndKeys:@"JiaoJiao",@"name", @"3",@"ID", @"no", @"checked", nil];
NSMutableDictionary *dict4 = [NSMutableDictionary dictionaryWithObjectsAndKeys:@"GuaZi",@"name", @"4",@"ID", @"yes", @"checked", nil];
[array addObject:dict1];
[array addObject:dict2];
[array addObject:dict3];
[array addObject:dict4];
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
[self initTest];
[cDS setTableView:tableView];
[cDS setAryCheck:array];
[tableView setDelegate:cDS];
[tableView setDataSource:cDS];
[tableView reloadData];
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
@end
现在运行一下我们的代码吧,看一下目前实现的效果如何,话不多说,直接上图吧:
现在我们可以看到我们的数据都出来了,但是第一栏并不是我们想要的复选框的效果,没办法,继续实现我们的代理方法吧。这里最主要的方法来了,就是下面这个啦。因为我们只需要将我们的第一栏设为复选框的形式,所以下面函数中只需要处理 identifier 为 checked 栏时的情况,仅当字典 checked 的值为 "yes" 和 “no” 时,将该cell 设置为 NSButtonCell,该buttoncell 会根据上面第二个代理方法设置的值显示 选中 和不选中两种状态。
- (NSCell *)tableView:(NSTableView *)tableView dataCellForTableColumn:(nullable NSTableColumn *)tableColumn row:(NSInteger)row
{
NSCell *cell = [tableColumn dataCell];
NSDictionary *dict = [aryCheck objectAtIndex:row];
NSString *identity = [tableColumn identifier];
id obj = nil;
if ([identity isEqualToString:@"checked"]){
obj = [dict valueForKey:@"checked"];
if ([obj isEqualToString:@"no"] || [obj isEqualToString:@"yes"]) {
NSButtonCell *btnCell = [[NSButtonCell alloc] init];
[btnCell setButtonType:NSSwitchButton];
[btnCell setTitle:@""];
cell = btnCell;
}
}
return cell;
}
现在继续运行我们的代码吧,看看效果如何吧!
哇奥,进行一番努力,我们的效果终于出来了,但是你点一下是不是没反应呢,这又是怎么了呢,原来我们还缺少一个代理方法,用于改变tableview 中的数据源。就是下面这个 setObjectValue 方法啦。这里我们将三列都设了数据源中对应值如何改变。name 栏和 ID 栏,直接通过 object 将对应改变后的字符串保存到数据源字典中的值中。而 checked 栏,我们根据选中和不选中两种状态将字典中对应 checked 键的值改为字符串 "yes" 和 “no”。
- (void)tableView:(NSTableView *)tableView setObjectValue:(nullable id)object forTableColumn:(nullable NSTableColumn *)tableColumn row:(NSInteger)row
{
NSLog(@"ddddddddddd");
NSMutableDictionary *dict = [aryCheck objectAtIndex:row];
NSString *identity = [tableColumn identifier];
if ([identity isEqualToString:@"name"]) {
[dict setValue:object forKey:@"name"];
NSLog(@"name");
return;
}
if ([identity isEqualToString:@"ID"]) {
[dict setValue:object forKey:@"ID"];
NSLog(@"ID");
return;
}
if ([identity isEqualToString:@"checked"]) {
NSLog(@"checked");
if ([object boolValue] == YES) {
[dict setValue:@"yes" forKey:@"checked"];
} else {
[dict setValue:@"no" forKey:@"checked"];
}
}
}
现在我们再运行我们的代码,是不是发现tableview 中的数据都可以改变了。做个小改变试一下吧。
==》
当然,我们的代码中设置了 Name 和 ID 栏可以改变的情况,但是你如果不像让这两个栏位的值可变,那直接在控件栏选中对应的 cell 设置它的 Behavior 为 None 吧。
现在我们的例子到此就介绍结束了,我们这个方法的好处时可以通过判断值来设置某一列的一些行为复选框,如果我们确定这一列就是要设为复选框,那么我们还有一个更简单的方法。现在开始看一下下面一个更简单的方法吧。
在窗口插入我们的 NSTableView 后,直接将第一列 Checked 栏的 NSTextFieldCell 替换为 NSButtonCell。如下图。
这时,我们就可以省去将 文本框 转换为 复选框的代理方法。这里直接贴上剩下两个主要的代理方法。
@interface checkDS : NSObject<NSTabViewDelegate,NSTableViewDataSource>
{
NSMutableArray *aryCheck;
NSButtonCell *myCell;
NSMutableArray *ary_result;
}
@property (retain) NSTableView *tableView;
- (void)setAry:(NSMutableArray *)tmpAry;
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
NSDictionary *data = [aryCheck objectAtIndex:row];
NSString *identity = [tableColumn identifier];
if ([identity isEqualToString:@"checked"]) {
myCell = [tableColumn dataCell];
[myCell setTitle:@""];
NSString *value = [data objectForKey:@"checked"];
if ([value isEqualToString:@"yes"]) {
[myCell setState:1];
} else {
[myCell setState:0];
}
return myCell;
} else {
return [data valueForKey:identity];
}
}
- (void)tableView:(NSTableView *)tableView setObjectValue:(id)anObject forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
NSLog(@"ddddddddddd");
NSMutableDictionary *dict = [aryCheck objectAtIndex:row];
NSString *identity = [tableColumn identifier];
if ([identity isEqualToString:@"name"]) {
[dict setValue:anObject forKey:@"name"];
NSLog(@"name array: %@", aryCheck);
return;
}
if ([identity isEqualToString:@"ID"]) {
[dict setValue:anObject forKey:@"ID"];
NSLog(@"ID array: %@", aryCheck);
return;
}
if ([identity isEqualToString:@"checked"]) {
myCell = [tableColumn dataCell];
if ([anObject boolValue] == YES) {
[dict setValue:@"yes" forKey:@"checked"];
} else {
[dict setValue:@"no" forKey:@"checked"];
}
NSLog(@"checked array: %@", aryCheck);
}
}
运行我们的代码,它可以实现和上面相同的效果。
Over, over 研究了一天终于彻底搞定了,幸幸苦苦码完第一篇博文,希望有缘人看到后可以轻松学会。