Search code examples
objective-cmacosnstableviewnstableviewcell

Dynamically created columns in NSTableView are empty


I have an NSTableView connected to my CountViewController.h

@property (nonatomic, weak) IBOutlet NSTableView * tableCountData;

Under -(void)viewDidAppear I have code to remove all columns, except the first column, and re-create with new ones.

-(void)viewDidAppear
{
    NSArray *tableColumns = [self.tableCountData tableColumns];
    BWProject *project = ((Document *)self.view.window.windowController.document).project;

    for (NSTableColumn *col in tableColumns) {
        if ([col.identifier isEqualToString:@"columnGenes"]) continue;
        [self.tableCountData removeTableColumn:col];
    }

    for (NSString *sample in project.samples) {
        NSTableColumn *col = [[NSTableColumn alloc] init];
        col.identifier = sample;
        col.headerCell.stringValue = sample;
        [self.tableCountData addTableColumn:col];
    }

    [self.tableCountData reloadData];
}

However, when I populate the table as follows:

- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
    BWProject *project = ((Document *)self.view.window.windowController.document).project;
    NSTableCellView *cellView = [tableView makeViewWithIdentifier:tableColumn.identifier owner:self];

    if (cellView == nil) {
        CGRect myRect = CGRectMake(0,0,tableColumn.width,0);
        cellView = [[NSTableCellView alloc] initWithFrame:myRect];
        [cellView addSubview:[[NSTextField alloc] initWithFrame:cellView.frame]];
    }

    if ([tableColumn.identifier isEqualToString:@"columnGenes"]) {
        cellView.textField.stringValue = [[project.geneset allKeys] objectAtIndex:row];
    } else {
        for (NSString *sample in project.samples) {
            if ([tableColumn.identifier isEqualToString:sample]) {
                BWGene *gene = [project.geneset objectForKey:[[project.geneset allKeys] objectAtIndex:row]];
                BWGeneReads *geneReads = [gene.samples objectForKey:sample];
                cellView.textField.stringValue = [NSString stringWithFormat:@"%f", geneReads.reads];
            }
        }
    }

    return cellView;
}

I get empty data columns. I have tried replacing [cellView addSubview:[[NSTextField alloc] initWithFrame:cellView.frame]] with [cellView setTextField:[[NSTextField alloc] initWithFrame:cellView.frame]] but this causes the app to crash with EXEC_BAD_ACCESS. Below is an example screenshot: Problematic NSTableView with empty columns

What am I doing wrong? I can't find any clues in the documentation but I feel like this is a simple solution as many apps use dynamic columns...


Solution

  • Don't do setDataCell: in a view based table view.

    If you create a new NSTableCellView in viewForTableColumn, you add a NSTextField subview. You have to set textField of the cell view to this text field.

    - (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
    {
        BWProject *project = ((Document *)self.view.window.windowController.document).project;
        NSTableCellView *cellView = [tableView makeViewWithIdentifier:tableColumn.identifier owner:self];
    
        if (cellView == nil) {
            CGRect myRect = CGRectMake(0,0,tableColumn.width,0);
            cellView = [[NSTableCellView alloc] initWithFrame:myRect];
            NSTextField *myTextField = [[NSTextField alloc] initWithFrame:cellView.frame];
            cellView.textField = myTextField; // this assignment was missing
            [cellView addSubview:myTextField];
        }
    
        if ([tableColumn.identifier isEqualToString:@"columnGenes"]) {
            cellView.textField.stringValue = [[project.geneset allKeys] objectAtIndex:row];
        } else {
            for (NSString *sample in project.samples) {
                if ([tableColumn.identifier isEqualToString:sample]) {
                    BWGene *gene = [project.geneset objectForKey:[[project.geneset allKeys] objectAtIndex:row]];
                    BWGeneReads *geneReads = [gene.samples objectForKey:sample];
                    cellView.textField.stringValue = [NSString stringWithFormat:@"%f", geneReads.reads];
                }
            }
        }
    
        return cellView;
    }