遍历 NSTableView 上的单元格

Iterating through cells on NSTableView

我正在构建一个 NSTableView 来显示文件路径数组 (directoriesArray)。我在 main.storyboard 上构建了 NSTableView 并且我使用了这段代码:

- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
    return _directoriesArray.count;
}

- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
    return [_directoriesArray objectAtIndex:row];
}

- (void)tableViewSelectionDidChange:(NSNotification *)notification {
    
    NSTableView *tableView = notification.object;
    NSLog(@"User has selected row %ld", (long)tableView.selectedRow);
    
    NSLog(@"%@",[_directoriesArray objectAtIndex:tableView.selectedRow]);
    
}

//changes font of everything
- (NSCell *)tableView:(NSTableView *)tableView dataCellForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
    NSTextFieldCell *cell = [tableColumn dataCell];
    [cell setFont:[self quicksand:15.0f]];
    return cell;
}

这非常有效。

我现在想遍历 NSTableView 上的每个单元格并使用 if 语句编辑它们。

我试过更换

- (NSCell *)tableView:(NSTableView *)tableView dataCellForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {

块,具有:

-(NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row{
    NSTableCellView *view = [tableView makeViewWithIdentifier:[tableColumn identifier] owner:self];
    if(view == nil){
        NSTableCellView *view = [[NSTableCellView alloc]initWithFrame:[tableView frame]];
        view.identifier = [tableColumn identifier];
    }
    NSTextField *textfield = [[NSTextField alloc]initWithFrame:NSMakeRect(0, 0, 100, 30)];
    [textfield setStringValue:[_directoriesArray objectAtIndex:tableView.selectedRow]];
    [textfield setBackgroundColor:[NSColor redColor]];
    [view addSubview:textfield];
    [view setNeedsDisplay:YES];
    return view;
}

但这显示的是空行!我做错了什么?!


奇怪的事情 - 当我在修改后的代码为 运行 时单击一个单元格时 tableViewSelectionDidChange 被调用并且我仍然完美地获得日志......?!


编辑

我已经更新了代码(尽我所能 - 这很糟糕):

-(NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row{
    NSTableCellView *view = [tableView makeViewWithIdentifier:[tableColumn identifier] owner:self];
    if(view == nil){
        NSTableCellView *view = [[NSTableCellView alloc]initWithFrame:[tableView frame]];
        view.identifier = [tableColumn identifier];
        view.translatesAutoresizingMaskIntoConstraints = false; //turned this off
        NSTextField *textfield;
        textfield.autoresizingMask = NSViewMaxXMargin | NSViewMinYMargin; //changed to autoresizing
        [textfield setStringValue:[_directoriesArray objectAtIndex:row]]; //changed to row
        [textfield setBackgroundColor:[NSColor redColor]];
        [view addSubview:textfield];
        [view setNeedsDisplay:YES];
    }
    return view;
}

这个问题好像有点糊涂。主题说它是关于遍历 table 视图的单元格,但它似乎与此无关。

您说您将 -tableView:dataCellForTableColumn:row: 方法替换为 -tableView:viewForTableColumn:row: 方法。第一个适用于基于单元格的 table 视图。第二个是基于视图的 table 视图。通过更改委托方法,您可以将 table 从基于单元格更改为基于视图。

对于以编程方式创建的 table 视图,我相信委托方法是唯一决定它是基于单元格还是基于视图的方法。但是,对于在情节提要中创建的 table 视图,也会在情节提要中指定。两者不匹配肯定会出事。

因此,如果您想从基于单元格切换到基于视图,请确保在情节提要中也进行更改。在这种情况下,您也可以在故事板中设置原型单元格视图。

不过,您是在代码中创建单元格视图。由于您没有关闭 translatesAutoresizingMaskIntoConstraints(默认情况下处于打开状态),因此您的单元格视图及其文本字段有效地使用了 springs-and-struts 模型进行布局。 table 视图将设置单元格视图的框架。但是,您最初将其设置得很大 — 与整个 table 视图一样大。此外,文本字段最初位于 (0, 0, 100, 30)。您需要设置它的 autoresizingMask 以确定当单元格视图的大小时它的大小应该如何改变。而且,如果它被设置为随着它的超级视图调整大小而调整大小,那么你会希望超级视图有一个更合理的大小开始(或者文本字段几乎不合理)。

您要避免的情况是单元格视图开始时很大,文本字段相对较小,文本字段配置为与单元格视图的大小大致成比例,所以当 table view 将单元​​格视图的大小调整为单元格的大小,将文本字段缩小到极小的尺寸。

在创建单元格视图之前,您要求 table 视图从其重用队列中获取一个。这很好。但是,如果它从其重用队列中获得一个,那么那个已经有一个文本字段。您不应该每次都创建和添加新的文本字段。文本字段的创建应在 if(view == nil) 语句内。

当您设置文本字段的 stringValue 时,您正在使用 tableView.selectedRow 索引到您的 _directoriesArray。这是错误的。首先,table 视图可能没有任何 selectedRow。事实上,首次设置时不太可能。我很惊讶你没有因为索引到带有错误索引 (-1) 的数组而出现异常。其次,为所有不同的行调用您正在实施的方法。你不想给他们所有相同的价值。该方法传递了一个 row 参数,该参数指示请求视图的行。

出于良好的考虑,我会将单元格视图的 textField 出口连接到您创建的文本字段。 NSTableCellView 有时会根据连接的 textField 插座做一些特殊的事情。 (例如,它使用文本字段的内容作为单元格的辅助功能文本。)


在您编辑的新代码中,出现了新问题。

您正在为单元格视图关闭 translatesAutoresizingMaskIntoConstraints。我不是建议你关掉它。我只是在解释它正在运行以及它的后果是什么。对于单元格视图,您应该将其保持打开状态,让 table 视图决定是否要将其关闭。通常,将视图插入视图层次结构的控制器代码决定是否将其关闭。但是,仍然有控制器代码不知道自动布局或 属性,因此默认情况下应将其保留。这样,自动布局感知控制器代码可以根据需要进行设置,而自动布局朴素控制器代码获得它所依赖的旧行为。 (从技术上讲,我们不能假设 NSTableView 的内部是否在这个意义上是自动布局感知的。)

您根本不再创建文本字段。您声明了变量,但没有创建实例。该变量仍然是 nil (假设您使用的是 ARC)。您仍然应该分配和初始化文本字段。但是,您应该为具有您希望它们具有的空间关系的单元格视图和文本字段使用 frame rect。 Springs-and-struts 只维持关系。所以,你需要从一段理智的关系开始,然后用autoresizingMask来维系。

如果您决定使用自动布局将文本字段与其超级视图(单元格视图)相关联,那么您可以在文本字段上关闭translatesAutoresizingMaskIntoConstraints .但是随后您需要建立约束以将文本字段与单元格视图相关联。在这种情况下,单元格视图和文本字段的初始帧将无关紧要。

在您的新代码中,您仅在创建文本字段时设置 stringValue。但是如果它也是从重用队列中获取的,则需要设置它。如果在创建两者时设置单元格视图的 textField 属性,这样做会容易得多。

这是您的代码的修订版:

-(NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row{
    NSTableCellView *view = [tableView makeViewWithIdentifier:[tableColumn identifier] owner:self];
    if(view == nil){
        NSTableCellView *view = [[NSTableCellView alloc]initWithFrame:NSMakeRect(0, 0, 100, 30)];
        view.identifier = [tableColumn identifier];
        NSTextField *textfield = [[NSTextField alloc] initWithFrame:NSInsetRect(view.frame, 2, 2)];
        textfield.autoresizingMask = NSViewMaxXMargin | NSViewMinYMargin; //changed to autoresizing
        textfield.backgroundColor = [NSColor redColor];
        [view addSubview:textfield];
        view.textField = textfield;
    }
    view.textField.stringValue = [_directoriesArray objectAtIndex:row];
    return view;
}