Search code examples
objective-ccocoacore-animationosx-mavericksnstableview

Semi-transparent NSTableView background drawing bug during animation


I have an view-based NSTableView with a semi-transparent background. This works fine, except for during row animations. If I use a fully-opaque color for the NSTableView background, then there is no problem. Here is a gif showing the problem:

http://gfycat.com/ChillyVigorousChamois

As the gif shows, during animation the background flickers a darker red than it should be. If you look closely, there is actually a section of the background that is drawn correctly, which slides down to accommodate the newly added row.

The NSScrollView behind the NSTableView is pure white, as set by this code:

-(void) awakeFromNib {
    self.drawsBackground = YES;
    self.backgroundColor = [NSColor whiteColor];
}

The NSTableView has 50% transparent red background, as drawn by this code:

-(BOOL) isOpaque {
    return NO;
}

- (void)drawBackgroundInClipRect:(NSRect)clipRect {
    [[[NSColor redColor] colorWithAlphaComponent:0.5] setFill];
    NSRectFillUsingOperation(clipRect, NSCompositeSourceOver);
}

The rows are animated with this code:

[_tableView insertRowsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:insertRange]
                  withAnimation:NSTableViewAnimationSlideLeft];

My best guess at the moment is that the darkening happens because the background is being repeatedly drawn over the top of itself during the animation. In the past, I've fixed a similar bug by adding the parent view to the NSAnimation, which forced it to be repeatedly redrawn along with the animating children. In this situation, I'm not sure how to do the same thing.

Any ideas how to fix this one?


Solution

  • This doesn't really fix the issue, but here is a workaround. The trick is to put the whole NSScrollView inside a custom NSView, and draw the background in the custom NSView.

    Make the NSTableView transparent:

    @implementation SJTableView
    - (BOOL)isOpaque {
        return NO;
    }
    
    -(void) drawBackgroundInClipRect:(NSRect)clipRect {
        NSRectFillUsingOperation(clipRect, NSCompositeClear);
    }
    @end
    

    Make the NSScrollView transparent:

    _scrollView.drawsBackground = NO;
    

    Draw the background in a custom view:

    @implementation SJView
    -(BOOL) isOpaque {
        return (_backgroundColor.alphaComponent >= 1.0);
    }
    
    -(void) drawRect:(NSRect)dirtyRect {
        [super drawRect:dirtyRect];
    
        if(_backgroundColor){
            [_backgroundColor setFill];
            NSRectFillUsingOperation(dirtyRect, NSCompositeSourceOver);
        }
    }
    @end