Checkbox 使用复选框管理NSTableView中的选择
我有一个NSTableView的经典设置,其中列绑定到NSArrayController的arrangedObjects的各种关键路径,NSArrayController绑定到一组字典 使用此设置,在tableview中选择一行或多行将自动工作。表视图和数组控制器一起工作,很容易查询NSArrayController以获得所选对象的列表 我的一个表列包含NSButtonCells,复选框类型。有没有办法使用Cocoa绑定将每行中的复选框绑定到该行的选择状态?我知道我可以向NSDictionary中添加另一个表示每行的值,但这会复制NSArrayController中已有的选择信息 如果有必要这样做的话,我也希望您能简要介绍一下您的实现Checkbox 使用复选框管理NSTableView中的选择,checkbox,nstableview,cocoa-bindings,nsarraycontroller,Checkbox,Nstableview,Cocoa Bindings,Nsarraycontroller,我有一个NSTableView的经典设置,其中列绑定到NSArrayController的arrangedObjects的各种关键路径,NSArrayController绑定到一组字典 使用此设置,在tableview中选择一行或多行将自动工作。表视图和数组控制器一起工作,很容易查询NSArrayController以获得所选对象的列表 我的一个表列包含NSButtonCells,复选框类型。有没有办法使用Cocoa绑定将每行中的复选框绑定到该行的选择状态?我知道我可以向NSDictionary
谢谢所以,这个问题的答案不适合胆小的人。原因是您试图让NSTableView执行它自然不想执行的操作 首先使用cocoa的原生NSTableView多选行为: -单击一行将选中该行并取消选择其他行 -按住控件并单击列仅切换该行的选择状态 现在,添加一列复选框。对于此行,规则不同: -单击复选框仅切换该行的选择状态 如果我们能够捕获复选框的点击并自己处理它们,这将很容易。现在我们可以了,但问题是在我们处理它们之后,它们仍然会被转发到NSTableView,以通常的方式更改选择。[注意:可能有一些方法可以避免此转发-如果您知道,请让我知道] 下面是您如何(最终)完成此任务的: -向基础对象数组中的每个对象添加“SelectedView”字段。将观察者添加到键路径的关联NSArrayController:“SelectedObject”。当选择更改时,为每个对象相应地设置SelectedView字段。大概是这样的:
if([keyPath isEqualToString:@"selectedObjects"]) {
// For table view checkbox's: keep "selectedInView" of song dictionaries up to date
[_arrayController.arrangedObjects enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
BOOL sel = [_arrayController.selectedObjects containsObject:obj];
if([[obj objectForKey:@"selectedInView"] boolValue] != sel)[obj setValue:[NSNumber numberWithBool:sel] forKey:@"selectedInView"];
}];
现在是棘手的部分:复选框出现故障的唯一时间是当已经存在选择时。以下是案例类型:
设置:选择行的1、2、3。选中第4行上的复选框。
结果:选中第四行的复选框。选择第四行。行的1、2、3被取消选择(因为NSTableView自然会这样做)
要解决此问题,无论何时单击复选框,您都需要创建一个临时数组来记住当前选择,加上或减去刚刚单击的复选框:
- (void)tableView:(NSTableView *)tableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
if([tableColumn.identifier isEqualToString:@"CheckBox"]) {
NSMutableDictionary *song = [_arrayController.arrangedObjects objectAtIndex:row];
if(!_tempSelectedSongs && _arrayController.selectedObjects) _tempSelectedSongs = [[NSMutableArray arrayWithArray:_arrayController.selectedObjects] retain];
if(_tempSelectedSongs) {
if([_tempSelectedSongs containsObject:song]) {
[_tempSelectedSongs removeObject:song];
} else if(![_tempSelectedSongs containsObject:song]) {
[_tempSelectedSongs addObject:song];
}
}
}
}
现在,在tableview完成选择处理之后,我们希望将选择设置为它应该是什么。有一个很有前景的函数,允许您在tableview实际执行选择之前修改它。您可以这样修改它:
- (NSIndexSet *)tableView:(NSTableView *)tableView selectionIndexesForProposedSelection:(NSIndexSet *)proposedSelectionIndexes {
NSMutableIndexSet *newSet = [NSMutableIndexSet indexSet];
if(_tempSelectedSongs) {
NSMutableIndexSet *indexSet = [NSMutableIndexSet indexSet];
[_tempSelectedSongs enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSUInteger index = [_arrayController.arrangedObjects indexOfObject:obj];
if(index != NSNotFound) [indexSet addIndex:index];
}];
proposedSelectionIndexes = indexSet;
[_tempSelectedSongs release]; _tempSelectedSongs = nil; [_tempSelectedSongsTimer invalidate]; [_tempSelectedSongsTimer release]; _tempSelectedSongsTimer = nil;
}
[proposedSelectionIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
NSProgressIndicator *progress = ((BDDiscreteProgressCell *)[[_arrayController.arrangedObjects objectAtIndex:idx] objectForKey:@"BDCell"]).progress;
if(!progress)
[newSet addIndex:idx];
}];
return newSet;
}
这非常有效,但是调用NSTableView委托函数的顺序有问题。显然,我们需要在使用信息的第二个函数之前调用第一个函数(在这里设置临时数组)
不管出于什么原因,当你取消选中一个复选框时,事情就是这样运行的,
但当您选中复选框时,情况正好相反。因此,对于这种情况,您可以向上述keyPath observer添加更多代码:
if([keyPath isEqualToString:@"selectedObjects"]) {
if(_tempSelectedSongs) {
_arrayController.selectedObjects = _tempSelectedSongs;
[_tempSelectedSongs release]; _tempSelectedSongs = nil;
}
// For table view checkbox's: keep "selectedInView" of song dictionaries up to date
[_arrayController.arrangedObjects enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
BOOL sel = [_arrayController.selectedObjects containsObject:obj];
if([[obj objectForKey:@"selectedInView"] boolValue] != sel)[obj setValue:[NSNumber numberWithBool:sel] forKey:@"selectedInView"];
}];
}
编辑:结果发现还有一种情况:如果选中了一行且其复选框为“未选中”,则不会自动触发SelectedObject通知,因此您必须在计时器上运行一个函数来实现新的选择。因此,这一问题的答案不适用于心脏虚弱的人。原因是您试图让NSTableView执行它自然不想执行的操作 首先使用cocoa的原生NSTableView多选行为: -单击一行将选中该行并取消选择其他行 -按住控件并单击列仅切换该行的选择状态 现在,添加一列复选框。对于此行,规则不同: -单击复选框仅切换该行的选择状态 如果我们能够捕获复选框的点击并自己处理它们,这将很容易。现在我们可以了,但问题是在我们处理它们之后,它们仍然会被转发到NSTableView,以通常的方式更改选择。[注意:可能有一些方法可以避免此转发-如果您知道,请让我知道] 下面是您如何(最终)完成此任务的: -向基础对象数组中的每个对象添加“SelectedView”字段。将观察者添加到键路径的关联NSArrayController:“SelectedObject”。当选择更改时,为每个对象相应地设置SelectedView字段。大概是这样的:
if([keyPath isEqualToString:@"selectedObjects"]) {
// For table view checkbox's: keep "selectedInView" of song dictionaries up to date
[_arrayController.arrangedObjects enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
BOOL sel = [_arrayController.selectedObjects containsObject:obj];
if([[obj objectForKey:@"selectedInView"] boolValue] != sel)[obj setValue:[NSNumber numberWithBool:sel] forKey:@"selectedInView"];
}];
现在是棘手的部分:复选框出现故障的唯一时间是当已经存在选择时。以下是案例类型:
设置:选择行的1、2、3。选中第4行上的复选框。
结果:选中第四行的复选框。选择第四行。行的1、2、3被取消选择(因为NSTableView自然会这样做)
要解决此问题,无论何时单击复选框,您都需要创建一个临时数组来记住当前选择,加上或减去刚刚单击的复选框:
- (void)tableView:(NSTableView *)tableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
if([tableColumn.identifier isEqualToString:@"CheckBox"]) {
NSMutableDictionary *song = [_arrayController.arrangedObjects objectAtIndex:row];
if(!_tempSelectedSongs && _arrayController.selectedObjects) _tempSelectedSongs = [[NSMutableArray arrayWithArray:_arrayController.selectedObjects] retain];
if(_tempSelectedSongs) {
if([_tempSelectedSongs containsObject:song]) {
[_tempSelectedSongs removeObject:song];
} else if(![_tempSelectedSongs containsObject:song]) {
[_tempSelectedSongs addObject:song];
}
}
}
}
现在,在tableview完成选择处理之后,我们希望将选择设置为它应该是什么。有一个很有前景的函数,允许您在tableview实际执行选择之前修改它。您可以这样修改它:
- (NSIndexSet *)tableView:(NSTableView *)tableView selectionIndexesForProposedSelection:(NSIndexSet *)proposedSelectionIndexes {
NSMutableIndexSet *newSet = [NSMutableIndexSet indexSet];
if(_tempSelectedSongs) {
NSMutableIndexSet *indexSet = [NSMutableIndexSet indexSet];
[_tempSelectedSongs enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSUInteger index = [_arrayController.arrangedObjects indexOfObject:obj];
if(index != NSNotFound) [indexSet addIndex:index];
}];
proposedSelectionIndexes = indexSet;
[_tempSelectedSongs release]; _tempSelectedSongs = nil; [_tempSelectedSongsTimer invalidate]; [_tempSelectedSongsTimer release]; _tempSelectedSongsTimer = nil;
}
[proposedSelectionIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
NSProgressIndicator *progress = ((BDDiscreteProgressCell *)[[_arrayController.arrangedObjects objectAtIndex:idx] objectForKey:@"BDCell"]).progress;
if(!progress)
[newSet addIndex:idx];
}];
return newSet;
}
这非常有效,但是调用NSTableView委托函数的顺序有问题。显然,我们需要在第二个函数-w之前调用第一个函数(在这里设置临时数组)