Search code examples
objective-cmacosnstableviewnstableviewcell

View based NSTableView selection highlighting


I am trying to port an iOS application onto the Mac and I came across a couple of issues during the transition. One of them is the customization of NSTableView. What exactly is the difference between NSCell, NSTableRowView and custom NSView based NSTableview ? I initially started out with a view based NSTableView, but I soon noticed that I would have to handle the selection myself. I could not pull that off, so I went on to use NSTableRowView, which, strangely, does not call the initialiser of my custom NSTableRowView.

I basically just want a custom table view cell with custom contents, which is selectable. What is the best way to do it ?

On iOS, I would just subclass UITableViewCell and set its selectedView property. On Mac this seems to be more complicated than that.


Solution

  • I have actually just found (in the sidebar) this question, which advises to subclass NSTableRowView. I had already done that before, but it did not work. I have tried it again and quite surprisingly it works now...

    Handling custom selection style in view based NSTableView

    However, this answer is not very informative, so I will try to cover what I have done in order to make this work.

    First of all, I implemented the following NSTableView delegate method and return nil!:

    - (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row{
    
        return nil;
    
    }
    

    In order to use a view based (I guess NSTableViewRow is regarded a view based table as well...) table you HAVE TO implement this method. I am not quite sure what I might have done wrong, but without this method, my cells are not displayed!

    Make sure to not let the NSTableView handle any selection by setting this property:

    yourNSTableView.selectionHighlightStyle = NSTableViewSelectionHighlightStyleNone;
    

    Okay, so now we want to set up our cells with the following delegate method:

    -(NSTableRowView *)tableView:(NSTableView *)tableView rowViewForRow:(NSInteger)row{
    
        static NSString *cellID = @"cell_identifier";
    
        //use this if you want to reuse the cells
        CustomTableRowView *result = [tableView makeViewWithIdentifier:cellID owner:self];
    
        if (result == nil) {
    
            result = [[CustomTableRowView alloc] initWithFrame:NSMakeRect(0, 0, self.frame.size.width, 80)];
            result.identifier = cellID;
    
        }
    
        result.data = [tableData objectAtIndex:row];
    
        // Return the result
        return result;
    
    }
    

    Okay so now subclass NSTableRowView and implement/override the following two methods:

    First we have to override setSelected: in order to make the cell redraw its background when it is selected. So here it is:

    -(void)setSelected:(BOOL)selected{
    
        [super setSelected:selected];
        [self setNeedsDisplay:YES];
    
    }
    

    As mentioned earlier, we call setNeedsDisplay: in order for the cell to redraw its background.

    Finally, the drawing code. Override the method drawBackgroundInRect: like this:

    -(void)drawBackgroundInRect:(NSRect)dirtyRect{
        if (!self.selected) {
            [[NSColor clearColor] set];
        } else {
            [someColor set];
        }
        NSRectFill(dirtyRect);
    }