如何使 NSTableView 行中的按钮响应所表示的对象
How can I make a button inside a NSTableViewRow respond to the represented object
我已经和这个问题斗争了很长一段时间。我正在开发一个文件复制管理器模块,到目前为止,除了取消按钮外,我已经能够使一切正常工作。出于某种原因,当我单击特定行的取消按钮时,按钮操作同时针对多行。
经过几天的研究,我能够使对象成功取消行所代表的操作:
-(IBAction)btnCancelOperationClick:(id)sender {
NSInteger row = [_tableView rowForView:sender];
if (row != -1) {
FileCopyOperation *opr = [_fileCopyOperations objectAtIndex:row];
[opr cancel];
[_fileCopyOperations removeObjectAtIndex:row];
[_tableView removeRowsAtIndexes:[NSIndexSet indexSetWithIndex:row] withAnimation:NSTableViewAnimationEffectFade];
}
}
效果很好,我可以取消操作并相应地更新我的 table。其他一切都按预期工作,但我的代码或绑定一定有问题。我从笔尖加载这个单元格,然后我使用以下方式注册这个笔尖:
[_tableView registerNib:[[NSNib alloc]initWithNibNamed:@"FileCopyCell" bundle:nil] forIdentifier:@"FileCopyCell"];
我将 QueueController 设置为文件的所有者并像这样连接所有内容:
如果有人能指出正确的方向让这个东西正常工作,我将不胜感激。提前致谢!
编辑以添加更多代码示例。
-(NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
FileCopyCell *cell = [tableView makeViewWithIdentifier:@"FileCopyCell" owner:self];
FileCopyOperation *opr = [_fileCopyOperations objectAtIndex:row];
[cell.fileName setStringValue:[NSString stringWithFormat:@"Copying \"%@\"",opr.fName]];
[cell.progressBar setDoubleValue:((opr.bWritten.doubleValue / opr.fSize.doubleValue) * 100)];
[cell.totalBytes setStringValue:[NSString stringWithFormat:@"of %@",[NSByteCountFormatter stringFromByteCount:opr.fSize.longLongValue countStyle:NSByteCountFormatterCountStyleFile]]];
[cell.status setStringValue:[NSString stringWithFormat:@"%@",[NSByteCountFormatter stringFromByteCount:opr.bWritten.longLongValue countStyle:NSByteCountFormatterCountStyleFile]]];
[cell.icon setImage:[[NSWorkspace sharedWorkspace]iconForFile:opr.srcURL.path]];
[cell.cancelButton setTarget:self];
return cell;
}
-(void)windowDidLoad {
[super windowDidLoad];
_fileCopyOperations = [NSMutableArray new];
windowFrame = [self.window frame];
rows = 0;
[_tableView registerNib:[[NSNib alloc]initWithNibNamed:@"FileCopyCell" bundle:nil] forIdentifier:@"FileCopyCell"];
if (!fileCopyManager) {
fileCopyManager = [FileCopyManager sharedFileCopyManager];
[fileCopyManager.fileCopyQueue addObserver:self forKeyPath:@"operationCount" options:NSKeyValueObservingOptionNew context:(void*)fileCopyManager];
}
[_scrollView setHasHorizontalScroller:NO];
[_scrollView setHasVerticalScroller:NO];
}
最好的方法是子类化 NSTableCellView
并让 它 处理它自己的动作和表示的对象。例如,表示 Foo
实例的单元格可以具有两个属性:foo
和 fooController
。当调用 (nonatomic
) foo
setter 时,单元格可以更新自己的 UI 来表示传递的 Foo
。当 Foo
控制器创建 table 单元时,它可以实例化一个 FooCell
实例,将自己设置为 fooController
并分配 Foo
实例并让 cell 自行处理。取消按钮的目标可以是它自己的单元格,当 -cancel:
动作被调用时,它可以告诉它的 fooController
做什么(因为 Foo
控制器负责更新队列和table 视图)并且由于它有对其 foo
的引用,它可以通过一些 -cancelFoo:(Foo *)theFoo
方法将其传递给控制器,而不依赖于控制器查找其索引(这可能如果您正在为行的出现和消失设置动画,或者用户正在快速连续取消一堆但它们的删除被延迟并异步更新,则不准确。
干净整洁。整洁有序的遏制/职责分离。单元格处理自己的 UI 更新和操作,并知道自己的 foo; foo 控制器处理其 foo 集合、table 视图以及将 foo 分配给 foo 单元格。
感谢 Joshua Nozzi,根据他的建议,我将按钮操作从控制器移到了单元格 class。在单元格 class 中,我使用下面的方法访问表示的对象并发送 [operation cancel]
消息。
-(IBAction)cancelOpr:(id)sender {
NSButton *button = (NSButton*)sender;
FileCopyOperation *opr = [(NSTableCellView*)[button superview]objectValue];
[opr cancel];
// This code calls the controller [removeObject:] method that takes care of cleaning everything out and updates the GUI accordingly.
AppDelegate *ad = (AppDelegate*)[[NSApplication sharedApplication]delegate];
QueueWindowController *qc = [ad getQueueController];
[qc.fileCopyOperations performSelectorOnMainThread:@selector(removeObject:) withObject:opr waitUntilDone:YES];
}
您可以获得完整的项目here。
我已经和这个问题斗争了很长一段时间。我正在开发一个文件复制管理器模块,到目前为止,除了取消按钮外,我已经能够使一切正常工作。出于某种原因,当我单击特定行的取消按钮时,按钮操作同时针对多行。
经过几天的研究,我能够使对象成功取消行所代表的操作:
-(IBAction)btnCancelOperationClick:(id)sender {
NSInteger row = [_tableView rowForView:sender];
if (row != -1) {
FileCopyOperation *opr = [_fileCopyOperations objectAtIndex:row];
[opr cancel];
[_fileCopyOperations removeObjectAtIndex:row];
[_tableView removeRowsAtIndexes:[NSIndexSet indexSetWithIndex:row] withAnimation:NSTableViewAnimationEffectFade];
}
}
效果很好,我可以取消操作并相应地更新我的 table。其他一切都按预期工作,但我的代码或绑定一定有问题。我从笔尖加载这个单元格,然后我使用以下方式注册这个笔尖:
[_tableView registerNib:[[NSNib alloc]initWithNibNamed:@"FileCopyCell" bundle:nil] forIdentifier:@"FileCopyCell"];
我将 QueueController 设置为文件的所有者并像这样连接所有内容:
如果有人能指出正确的方向让这个东西正常工作,我将不胜感激。提前致谢!
编辑以添加更多代码示例。
-(NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
FileCopyCell *cell = [tableView makeViewWithIdentifier:@"FileCopyCell" owner:self];
FileCopyOperation *opr = [_fileCopyOperations objectAtIndex:row];
[cell.fileName setStringValue:[NSString stringWithFormat:@"Copying \"%@\"",opr.fName]];
[cell.progressBar setDoubleValue:((opr.bWritten.doubleValue / opr.fSize.doubleValue) * 100)];
[cell.totalBytes setStringValue:[NSString stringWithFormat:@"of %@",[NSByteCountFormatter stringFromByteCount:opr.fSize.longLongValue countStyle:NSByteCountFormatterCountStyleFile]]];
[cell.status setStringValue:[NSString stringWithFormat:@"%@",[NSByteCountFormatter stringFromByteCount:opr.bWritten.longLongValue countStyle:NSByteCountFormatterCountStyleFile]]];
[cell.icon setImage:[[NSWorkspace sharedWorkspace]iconForFile:opr.srcURL.path]];
[cell.cancelButton setTarget:self];
return cell;
}
-(void)windowDidLoad {
[super windowDidLoad];
_fileCopyOperations = [NSMutableArray new];
windowFrame = [self.window frame];
rows = 0;
[_tableView registerNib:[[NSNib alloc]initWithNibNamed:@"FileCopyCell" bundle:nil] forIdentifier:@"FileCopyCell"];
if (!fileCopyManager) {
fileCopyManager = [FileCopyManager sharedFileCopyManager];
[fileCopyManager.fileCopyQueue addObserver:self forKeyPath:@"operationCount" options:NSKeyValueObservingOptionNew context:(void*)fileCopyManager];
}
[_scrollView setHasHorizontalScroller:NO];
[_scrollView setHasVerticalScroller:NO];
}
最好的方法是子类化 NSTableCellView
并让 它 处理它自己的动作和表示的对象。例如,表示 Foo
实例的单元格可以具有两个属性:foo
和 fooController
。当调用 (nonatomic
) foo
setter 时,单元格可以更新自己的 UI 来表示传递的 Foo
。当 Foo
控制器创建 table 单元时,它可以实例化一个 FooCell
实例,将自己设置为 fooController
并分配 Foo
实例并让 cell 自行处理。取消按钮的目标可以是它自己的单元格,当 -cancel:
动作被调用时,它可以告诉它的 fooController
做什么(因为 Foo
控制器负责更新队列和table 视图)并且由于它有对其 foo
的引用,它可以通过一些 -cancelFoo:(Foo *)theFoo
方法将其传递给控制器,而不依赖于控制器查找其索引(这可能如果您正在为行的出现和消失设置动画,或者用户正在快速连续取消一堆但它们的删除被延迟并异步更新,则不准确。
干净整洁。整洁有序的遏制/职责分离。单元格处理自己的 UI 更新和操作,并知道自己的 foo; foo 控制器处理其 foo 集合、table 视图以及将 foo 分配给 foo 单元格。
感谢 Joshua Nozzi,根据他的建议,我将按钮操作从控制器移到了单元格 class。在单元格 class 中,我使用下面的方法访问表示的对象并发送 [operation cancel]
消息。
-(IBAction)cancelOpr:(id)sender {
NSButton *button = (NSButton*)sender;
FileCopyOperation *opr = [(NSTableCellView*)[button superview]objectValue];
[opr cancel];
// This code calls the controller [removeObject:] method that takes care of cleaning everything out and updates the GUI accordingly.
AppDelegate *ad = (AppDelegate*)[[NSApplication sharedApplication]delegate];
QueueWindowController *qc = [ad getQueueController];
[qc.fileCopyOperations performSelectorOnMainThread:@selector(removeObject:) withObject:opr waitUntilDone:YES];
}
您可以获得完整的项目here。