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 的更新进行更新

几点:

  1. 使用 table 列的标识符作为单元格视图的标识符有点奇怪,因为两者可能不同。特别是如果您在同一列中有多种可能的单元格类型(常见情况)。最好练习为每个单元格视图使用唯一标识符,如果您有多个单元格视图,则仅使用列标识符来标识列。

  2. 你让你的观点之外if()你的if()有条件检查table正在询问。如果您只有一个 table,请不要检查;如果您可能有多个使用该控制器的控制器,请确保与每个 table 有关的所有内容(包括 -makeViewWithIdentifier:) 都在其相关条件内。 returning myView 相同 - 应该进入你的条件,如果没有 table 匹配,你的最后一个 return 应该是 nil 最佳实践。

  3. 您似乎仅在 row == 0 时才配置您的单元格视图,但您未能说明已配置为第 0 行单元格但正在为一行重新使用的重用视图 > 0。您可能会在需要大量滚动的大型 table 中看到所有 >0 行的 "interesting" 行为。

  4. 您确定单元格视图的第一个子视图是您的NSTextField吗?您是否在调试器控制台中看到任何消息(如“...does not respond to selector...”)?或者,如果您在 [myTextField setEditable:NO] 行上设置断点,当调试器在那里暂停时 myTextField 是否为 nil?这里的最佳做法是让您自己的视图子 class 与 IBOutlets 到您想要与之交互的任何子视图,然后以这种方式更新它们。依赖其子视图列表会在您编辑单元格视图时导致混淆和未来的错误,即使它一开始是有效的。

  5. 与4相关,如果你只需要一个文本字段作为你的单元格,为什么不使用stock NSTableCellView class(它有自己的-textField出口)所以你可以告诉它 myView.textField.attributedStringValue = someAttributedString?

  6. NSTextField 有单独的方法来设置其字符串值的字体和颜色。为什么不利用这些而不是创建和设置属性字符串?

  7. 补充:忘了你提到绑定。即使您正确获取了对文本字段的引用,在此处设置其属性也无济于事,因为绑定可能会覆盖它们。更容易放弃绑定并与 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;
}