objective-cmacosnstablerowview

View-based NSTableView selection?


I wrote a view-based tableview, like this: enter image description here

and I draw selection with NSTableRowView, code is like this:

- (void)drawRect:(NSRect)dirtyRect {
  [[NSColor clearColor] setFill];
  if (self.isClicked) {
    [[[NSColor blackColor] colorWithAlphaComponent:0.08] setFill];
  }
  NSRect rowViewRect = NSMakeRect(0, 0, 274, 72);
  NSBezierPath *path = [NSBezierPath bezierPathWithRect:rowViewRect];
  [path fill];
}

But finally, I found the TableRowView is not over the tableView, so the selectedColor not cover the image and the button, it more like background color, but I need the selected TableRowView cover the view, just like this:

enter image description here

The selected color cover the image and the button. I have googled, but not found any ideas. Thanks for help~


Solution

  • So this is a bit tricky. The strategy is to have an overlay colored view with alpha less than 1 in your NSTableCellView and then add and remove it based on the cell's selection.

    First, you need an NSView that can set a background color:

    NSView_Background.h

    @interface NSView_Background : NSView 
    @property (nonatomic, strong) NSColor *background;
    @end
    

    NSView_Background.m

    #import "NSView_Background.h"
    
    @implementation NSView_Background
    
    - (void)drawRect:(NSRect)dirtyRect {
        [self.background set];
        NSRectFill([self bounds]);
    }
    
    - (void)setBackground:(NSColor *)color {
        if ([_background isEqual:color]) return;
    
        _background = color;
    
        [self setNeedsDisplay:YES];
    }
    @end
    

    Second, in your NSTableCellView subclass, add a NSView_Background property:

    #import "NSView_Background.h"
    
    @interface
    @property (nonatomic, strong) NSView_Background *selectionView;
    @end
    

    Third, add this method to NSTableCellView subclass:

    - (void)shouldShowSelectionView:(BOOL)shouldShowSelectionView {
        if (shouldShowSelectionView) {
            self.selectionView = [[NSView_Background alloc] init];
            [self.selectionView setBackground:[NSColor grayColor]];
            self.selectionView.alpha = 0.4;
            [self addSubview:self.selectionView];
    
            [self setNeedsDisplay:YES];   // draws the selection view
        } else {
            [self.selectionView removeFromSuperview];
            self.selectionView = nil;
        }
    }
    

    Fourth, add this to drawRect in your NSTableCellView subclass:

    - (void)drawRect:(NSRect)dirtyRect {
        if (self.selectionView)
            self.selectionView.frame = self.bounds;
    }
    

    Finally, override NSTableCellView:setBackgroundStyle:

    - (void)setBackgroundStyle:(NSBackgroundStyle)backgroundStyle {
        switch (backgroundStyle) {
            case: NSBackgroundStyleDark:
                [self shouldShowSelectionView:YES];
                break;
            default:
                [self shouldShowSelectionView:NO];
                break;
        }
    }
    

    I know this seems hacky but this is the only way I could get this behavior. Hope this helps and good luck!