Cocoa 如何在基于 NSTableView 的视图中对特定行实施特定的编辑行为?
Cocoa How to enforce specific editing behaviour to a particular row in a view based NSTableView?
我有一个基于 NSTableView 的简单视图,其中一列填充有核心数据实体,而行的视图仅包含一个 NSTextField。我只需要第一行是非 editable,并以红色显示。我试图在 applicationDidFinishLaunching 的某个地方做到这一点:
NSView *myView = [myPlaylistsTableView viewAtColumn:0 row:0 makeIfNecessary:NO];
NSArray *mySubviews = [myView subviews];
NSTextField *myTextField = [mySubviews firstObject];
[myTextField setEditable:NO];
NSDictionary *myDictionary = [NSDictionary dictionaryWithObjectsAndKeys:[NSColor redColor],NSForegroundColorAttributeName, nil];
NSAttributedString *myRedAttributedString = [[NSAttributedString alloc] initWithString:@"All Songs" attributes:myDictionary];
[myTextField setAttributedStringValue:myRedAttributedString];
但是,一旦在 table 中添加了新元素,或者执行了拖动操作,第一行就会再次获得 editable。我尝试编写一个值转换器,将 NSTextField editable 绑定到数组控制器 selectionIndex,如果 selectionIndex 为 0,则 return NO,在所有其他情况下是。这不起作用,具有值绑定的有条件设置 editable 复选框的各种配置。我想 NSTableView 的一个子类也应该可以解决这个问题,但我在这方面有点迷茫。
任何帮助一如既往地非常感谢。谢谢
根据@Joshua Nozzi 的建议,我正在为 table 视图实现此委托方法:
-(NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
NSView *myView = [tableView makeViewWithIdentifier:[tableColumn identifier] owner:self];
if (tableView == myPlaylistsTableView)
{
if (row == 0)
{
NSArray *mySubviews = [myView subviews];
NSTextField *myTextField = [mySubviews firstObject];
[myTextField setEditable:NO];
NSDictionary *myDictionary = [NSDictionary dictionaryWithObjectsAndKeys:[NSColor redColor],NSForegroundColorAttributeName, nil];
NSAttributedString *myBoldAllSongsString = [[NSAttributedString alloc] initWithString:@"All Songs" attributes:myDictionary];
[myTextField setAttributedStringValue:myBoldAllSongsString];
}
}
return myView;
}
但我一定遗漏了一些东西,因为为正确的 NSTableView 和 row = 0 调用了该方法,代码被执行但我仍然没有红色字符串并且文本字段是 editable。
再次感谢您的帮助。
您正在尝试定位一个单元格视图并对其进行设置 "forever." NSTableView
这种方式不太可行。每次重新加载一行或整个 table 时,受影响行的视图为 "refreshed"。
自定义它们的适当位置是在 NSTableViewDelegate
协议方法 -tableView:viewForTableColumn:row:
中(记住将实现此方法的控制器设置为 table 视图的委托)。
根据 OP 的更新进行更新
几点:
使用 table 列的标识符作为单元格视图的标识符有点奇怪,因为两者可能不同。特别是如果您在同一列中有多种可能的单元格类型(常见情况)。最好练习为每个单元格视图使用唯一标识符,如果您有多个单元格视图,则仅使用列标识符来标识列。
你让你的观点在之外if()
你的if()
有条件检查table正在询问。如果您只有一个 table,请不要检查;如果您可能有多个使用该控制器的控制器,请确保与每个 table 有关的所有内容(包括 -makeViewWithIdentifier:) 都在其相关条件内。 returning myView
相同 - 应该进入你的条件,如果没有 table 匹配,你的最后一个 return 应该是 nil 最佳实践。
您似乎仅在 row == 0
时才配置您的单元格视图,但您未能说明已配置为第 0 行单元格但正在为一行重新使用的重用视图 > 0。您可能会在需要大量滚动的大型 table 中看到所有 >0 行的 "interesting" 行为。
您确定单元格视图的第一个子视图是您的NSTextField
吗?您是否在调试器控制台中看到任何消息(如“...does not respond to selector...
”)?或者,如果您在 [myTextField setEditable:NO]
行上设置断点,当调试器在那里暂停时 myTextField
是否为 nil?这里的最佳做法是让您自己的视图子 class 与 IBOutlet
s 到您想要与之交互的任何子视图,然后以这种方式更新它们。依赖其子视图列表会在您编辑单元格视图时导致混淆和未来的错误,即使它一开始是有效的。
与4相关,如果你只需要一个文本字段作为你的单元格,为什么不使用stock NSTableCellView class(它有自己的-textField
出口)所以你可以告诉它 myView.textField.attributedStringValue = someAttributedString
?
NSTextField
有单独的方法来设置其字符串值的字体和颜色。为什么不利用这些而不是创建和设置属性字符串?
补充:忘了你提到绑定。即使您正确获取了对文本字段的引用,在此处设置其属性也无济于事,因为绑定可能会覆盖它们。更容易放弃绑定并与 NSTableViewDelegate
一起实施 NSTableDataSource
协议,并在数据更改时自行处理刷新 table 视图。关于 UI 更新的时间安排,您会省去很多麻烦和困惑。
基于上述重写的例子:
-(NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
// Is it the right table view?
if (tableView == myPlaylistsTableView)
{
// Determine properties based on row
BOOL firstRow = (row == 0);
NSFont * font = (firstRow) ? [NSFont systemFontOfSize:[NSFont systemFontSize]] : [NSFont boldSystemFontOfSize:[NSFont systemFontSize]];
NSString * text = (firstRow) ? @"All Songs" : [self.songsList[ row ] songTitle]; // or whatever other row titles will be
NSColor * color = (firstRow) ? [NSColor redColor] : [NSColor labelColor];
// Make an configure a cell view
NSTableCellView * myView = [tableView makeViewWithIdentifier:@"TextCell" owner:nil];
NSTextField * textField = myView.textField;
textField.editable = !firstRow;
textField.stringValue = text;
textField.font = font;
textField.textColor = color;
return myView;
}
return nil;
}
我有一个基于 NSTableView 的简单视图,其中一列填充有核心数据实体,而行的视图仅包含一个 NSTextField。我只需要第一行是非 editable,并以红色显示。我试图在 applicationDidFinishLaunching 的某个地方做到这一点:
NSView *myView = [myPlaylistsTableView viewAtColumn:0 row:0 makeIfNecessary:NO];
NSArray *mySubviews = [myView subviews];
NSTextField *myTextField = [mySubviews firstObject];
[myTextField setEditable:NO];
NSDictionary *myDictionary = [NSDictionary dictionaryWithObjectsAndKeys:[NSColor redColor],NSForegroundColorAttributeName, nil];
NSAttributedString *myRedAttributedString = [[NSAttributedString alloc] initWithString:@"All Songs" attributes:myDictionary];
[myTextField setAttributedStringValue:myRedAttributedString];
但是,一旦在 table 中添加了新元素,或者执行了拖动操作,第一行就会再次获得 editable。我尝试编写一个值转换器,将 NSTextField editable 绑定到数组控制器 selectionIndex,如果 selectionIndex 为 0,则 return NO,在所有其他情况下是。这不起作用,具有值绑定的有条件设置 editable 复选框的各种配置。我想 NSTableView 的一个子类也应该可以解决这个问题,但我在这方面有点迷茫。 任何帮助一如既往地非常感谢。谢谢
根据@Joshua Nozzi 的建议,我正在为 table 视图实现此委托方法:
-(NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
NSView *myView = [tableView makeViewWithIdentifier:[tableColumn identifier] owner:self];
if (tableView == myPlaylistsTableView)
{
if (row == 0)
{
NSArray *mySubviews = [myView subviews];
NSTextField *myTextField = [mySubviews firstObject];
[myTextField setEditable:NO];
NSDictionary *myDictionary = [NSDictionary dictionaryWithObjectsAndKeys:[NSColor redColor],NSForegroundColorAttributeName, nil];
NSAttributedString *myBoldAllSongsString = [[NSAttributedString alloc] initWithString:@"All Songs" attributes:myDictionary];
[myTextField setAttributedStringValue:myBoldAllSongsString];
}
}
return myView;
}
但我一定遗漏了一些东西,因为为正确的 NSTableView 和 row = 0 调用了该方法,代码被执行但我仍然没有红色字符串并且文本字段是 editable。
再次感谢您的帮助。
您正在尝试定位一个单元格视图并对其进行设置 "forever." NSTableView
这种方式不太可行。每次重新加载一行或整个 table 时,受影响行的视图为 "refreshed"。
自定义它们的适当位置是在 NSTableViewDelegate
协议方法 -tableView:viewForTableColumn:row:
中(记住将实现此方法的控制器设置为 table 视图的委托)。
根据 OP 的更新进行更新
几点:
使用 table 列的标识符作为单元格视图的标识符有点奇怪,因为两者可能不同。特别是如果您在同一列中有多种可能的单元格类型(常见情况)。最好练习为每个单元格视图使用唯一标识符,如果您有多个单元格视图,则仅使用列标识符来标识列。
你让你的观点在之外
if()
你的if()
有条件检查table正在询问。如果您只有一个 table,请不要检查;如果您可能有多个使用该控制器的控制器,请确保与每个 table 有关的所有内容(包括 -makeViewWithIdentifier:) 都在其相关条件内。 returningmyView
相同 - 应该进入你的条件,如果没有 table 匹配,你的最后一个 return 应该是 nil 最佳实践。您似乎仅在
row == 0
时才配置您的单元格视图,但您未能说明已配置为第 0 行单元格但正在为一行重新使用的重用视图 > 0。您可能会在需要大量滚动的大型 table 中看到所有 >0 行的 "interesting" 行为。您确定单元格视图的第一个子视图是您的
NSTextField
吗?您是否在调试器控制台中看到任何消息(如“...does not respond to selector...
”)?或者,如果您在[myTextField setEditable:NO]
行上设置断点,当调试器在那里暂停时myTextField
是否为 nil?这里的最佳做法是让您自己的视图子 class 与IBOutlet
s 到您想要与之交互的任何子视图,然后以这种方式更新它们。依赖其子视图列表会在您编辑单元格视图时导致混淆和未来的错误,即使它一开始是有效的。与4相关,如果你只需要一个文本字段作为你的单元格,为什么不使用stock NSTableCellView class(它有自己的
-textField
出口)所以你可以告诉它myView.textField.attributedStringValue = someAttributedString
?NSTextField
有单独的方法来设置其字符串值的字体和颜色。为什么不利用这些而不是创建和设置属性字符串?补充:忘了你提到绑定。即使您正确获取了对文本字段的引用,在此处设置其属性也无济于事,因为绑定可能会覆盖它们。更容易放弃绑定并与
NSTableViewDelegate
一起实施NSTableDataSource
协议,并在数据更改时自行处理刷新 table 视图。关于 UI 更新的时间安排,您会省去很多麻烦和困惑。
基于上述重写的例子:
-(NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
// Is it the right table view?
if (tableView == myPlaylistsTableView)
{
// Determine properties based on row
BOOL firstRow = (row == 0);
NSFont * font = (firstRow) ? [NSFont systemFontOfSize:[NSFont systemFontSize]] : [NSFont boldSystemFontOfSize:[NSFont systemFontSize]];
NSString * text = (firstRow) ? @"All Songs" : [self.songsList[ row ] songTitle]; // or whatever other row titles will be
NSColor * color = (firstRow) ? [NSColor redColor] : [NSColor labelColor];
// Make an configure a cell view
NSTableCellView * myView = [tableView makeViewWithIdentifier:@"TextCell" owner:nil];
NSTextField * textField = myView.textField;
textField.editable = !firstRow;
textField.stringValue = text;
textField.font = font;
textField.textColor = color;
return myView;
}
return nil;
}