evdude100
evdude100

Reputation: 437

NSTableView Cell Display Issue

I'm using a view-based NSTableView, and I've ran across a little issue.

I'm trying to switch the text color of my two labels from black to white when highlighted.

To do so, I've written the following code,

- (void)tableViewSelectionDidChange:(NSNotification *)notification
{
    NSView * viewInQuestion = [table viewAtColumn:0 row:[table selectedRow] makeIfNecessary:YES];

    if ([viewInQuestion isNotEqualTo:lastViewSelected])
    {
        [(NSTextField*)lastViewSelected.subviews.lastObject setTextColor:NSColor.blackColor];
        [(NSTextField*)[lastViewSelected.subviews objectAtIndex:1] setTextColor:NSColor.grayColor];
    }

    [(NSTextField*)viewInQuestion.subviews.lastObject setTextColor:NSColor.whiteColor];
    [(NSTextField*)[viewInQuestion.subviews objectAtIndex:1] setTextColor:NSColor.whiteColor];

    lastViewSelected = viewInQuestion;
}

That works great; I get this result:

The issue is that sometimes the text doesn't appear white even though an NSLog told me that the NSTextField's color was NSCalibratedWhite (or whatever it's called).

The color also switches back to black when the textField is not visible (scrolling away from it and then back). Yet again, even when it does this, the NSTextField's color is still logged as white.

Upvotes: 1

Views: 712

Answers (2)

Andrey Tarantsov
Andrey Tarantsov

Reputation: 9073

Overriding setBackgroundStyle on NSTableViewCell has worked perfectly for me, at least on OS X 10.8. (Given the number of relevant questions here on SO, one can guess that there used to be some problems before.)

The background style is updated on selection events and on window activation/deactivation, just as one would expect.

Here's my custom cell impl — as trivial as it can get:

@implementation RuntimeInstanceCellView

- (void)setBackgroundStyle:(NSBackgroundStyle)backgroundStyle {
    [super setBackgroundStyle:backgroundStyle];
    self.detailTextField.textColor = (backgroundStyle == NSBackgroundStyleLight ? [NSColor darkGrayColor] : [NSColor colorWithCalibratedWhite:0.85 alpha:1.0]);
//    self.detailTextField.textColor = (backgroundStyle == NSBackgroundStyleLight ? [NSColor blackColor] : [NSColor whiteColor]);
}

@end

Upvotes: 1

evdude100
evdude100

Reputation: 437

My method is very hacky, and probably not the optimal solution; But it resolves it so that's good.

Assuming you implemented tableSelectionDidChange the way I have, all you need to do is register an NSNotification and implement a custom method that should be more explicit.

In the init, awake, or didFinishLaunching part of your application...

NSView * contentView = table.enclosingScrollView.contentView;
[contentView setPostsFrameChangedNotifications:YES];
[NSNotificationCenter.defaultCenter addObserver:self selector:@selector(boundsDidChange:) name:NSViewBoundsDidChangeNotification object:contentView];

Somewhere else in the program...

(assuming hasUpdatedCell is a BOOLEAN property)

- (void)boundsDidChange:(NSNotification *)notification
{
    /* Bounds can change while nothing is selected--> but we only want to execute the method if a cell is selected. */ 

    if ([table selectedRow] == -1) {return;}

    NSRect visibleRect = table.enclosingScrollView.visibleRect;
    NSView * viewInQuestion = [table viewAtColumn:0 row:[table selectedRow] makeIfNecessary:YES];
    NSPoint selectedViewOrigin = [viewInQuestion convertPoint:viewInQuestion.frame.origin toView:table.enclosingScrollView];

    /* If the selected cell is visible, then we can go ahead and redraw the white text as a part of the workaround.
       This is because scrolling away from the selected cell and back will make the cell revert back to black. */

    BOOL cellVisible = NSPointInRect(selectedViewOrigin, visibleRect);

    /* We already know we need to update it, and we will so we don't need to evaluate the next step in the program */

    if (!cellVisible && !hasUpdatedCell) {return;}


    if (cellVisible && !hasUpdatedCell)
    {
        /* The cell is visible but we haven't updated. Let's do it then. */

        [self tableViewSelectionDidChange:nil];
        hasUpdatedCell = YES;
    } 
    else if (!cellVisible)
    {
        /* The cell is not visible and we need to update next time. */

        hasUpdatedCell = NO;
    }
}

Things then should get displayed properly.

Upvotes: 0

Related Questions