在NSTableView中只为已填充的行绘制网格线?

25

我该如何在NSTableView中仅为已填充行绘制网格线?当我将网格样式掩码设置为NSTableViewSolidHorizontalGridStyleMask时,它会为所有行绘制网格线,无论是否有数据。

我正在寻找一些示例代码来实现这一点。

4个回答

44

我发现以下代码对我最有效。只需欺骗原始网格绘制代码,使其仅在填充的行上进行绘制。

如有需要,请子类化NSTableView并覆盖drawGridInClipRect:(NSRect)clipRect方法,具体如下:

- (void)drawGridInClipRect:(NSRect)clipRect
{
    NSRect lastRowRect = [self rectOfRow:[self numberOfRows]-1];
    NSRect myClipRect = NSMakeRect(0, 0, lastRowRect.size.width, NSMaxY(lastRowRect));
    NSRect finalClipRect = NSIntersectionRect(clipRect, myClipRect);
    [super drawGridInClipRect:finalClipRect];
}

不必像上面那样计算myClipRect,你可以这样做:NSRect finalClipRect = NSIntersectionRect(clipRect, self.bounds)。 - sam
@sam 你可以尝试这个,但我认为它不会起作用。它没有考虑到最后一行的位置。Self是表视图,它可能比表中所有行都高。事实上,按定义,clipRect应该已经小于或等于self.bounds。 - Jacob Gorban
这将在空白的白色区域中创建一个黑色的视图。我注意到当我升级到xcode 5.0.1时出现了这个问题。有任何更新的答案吗? - Xander
为了在最后一列之后也裁剪网格,您可以使用以下代码:NSRect lastColumnRect = [self rectOfColumn:self.numberOfColumns - 1]; NSRect finalClipRect = NSMakeRect(0, 0, NSMaxX(lastColumnRect), NSMaxY(lastRowRect)); - Lukáš Kubánek
运行得像魔法一样!你是我的救星! - Tommy
1
我正在寻找另一种解决方案。 我想要禁用某些部分的网格线。我不希望它们在部分之间画出来,只想在单元格之间画出来。有什么好的想法吗? - Sid

20

在Swift 3.1中,这就是你所需要的:

import Cocoa

class GridClipTableView: NSTableView {

    override func drawGrid(inClipRect clipRect: NSRect) { }

}

2
同 Obj-C 一样:通过覆盖 drawGridInClipRect: 方法并使用空方法,只会绘制网格项。 - Alessandro
1
这个完美地运行了 - 而且比其他解决方案更加简洁! - Aaron Frary

2
我认为你需要对 NSTableView 进行子类化并重写 drawRow:clipRect:。另一个可能的选择是覆盖 drawGridInClipRect:,但那会更加麻烦,因为你需要手动挑选出行。

这应该能帮助你朝着正确的方向前进:

- (void)drawRow:(NSInteger)rowIndex clipRect:(NSRect)clipRect {
    // First do the default drawing
    [super drawRow:rowIndex clipRect:clipRect];

    // Check if there's content in any of the cells in this row
    BOOL doesHaveContent = NO;
    int numCols = [self numberOfColumns];
    int colIndex;
    for( colIndex = 0; colIndex < numCols; colIndex++ ){
        NSCell * cell = [self preparedCellAtColumn:colIndex 
                                               row:rowIndex];
        if( [cell objectValue] != nil ){
            doesHaveContent = YES;
            break;
        }
    }

    if( doesHaveContent ){
        NSRect rowRect = [self rectOfRow:rowIndex];
        NSBezierPath * gridPath = [NSBezierPath bezierPath];
        // Ignoring clipRect because this isn't a lot of drawing
        [gridPath moveToPoint:rowRect.origin];
        [gridPath lineToPoint:NSMakePoint(rowRect.origin.x + rowRect.size.width,
                                          rowRect.origin.y)];
        [gridPath moveToPoint:NSMakePoint(rowRect.origin.x,
                                          rowRect.origin.y + rowRect.size.height)];
        [gridPath lineToPoint:NSMakePoint(rowRect.origin.x + rowRect.size.width,
                                          rowRect.origin.y + rowRect.size.height)];
        [myGridColor set];
        [gridPath stroke];
        // You could also just do:
        // [myGridColor set];
        // [[NSBezierPath bezierPathWithRect:rowRect] stroke];
    }
}

另一个可能性是使用自定义的NSCell;如果每个单元格都有内容,那么每个单元格都可以在其周围绘制特殊边框。但是,如果有一些单元格为空,则不会在整行上绘制一条线。

另一种可能性是根据表视图数据源中的实际行数调整表视图的大小。 - user557219
1
@Bavarious:很好的观点,但我认为OP希望存在空行,只是不希望它们对齐。 - jscs
完全不起作用。DrawRow方法根本没有被调用 :( - Vlad E. Borovtsov

1

这里列出的两种解决方法都不适用于我,不知道为什么。我所做的是:

  1. 子类化NSTableView并重写rectOfColumn:(见下文)。
  2. 在表视图、其剪辑视图和滚动视图上开启层支持(这是必需的)。

这在我的测试中从macOS 10.9到10.15都有效。

- (NSRect)rectOfColumn:(NSInteger)column
{
    NSRect value = [super rectOfColumn:column];

    NSInteger numberOfRows = [self numberOfRows];
    if (numberOfRows == 0) {
        value.size.height = 0;
    } else {
        NSRect firstRect = [self rectOfRow:0];
        NSRect lastRect = [self rectOfRow:numberOfRows - 1];

        value.size.height = NSMaxY(lastRect) - NSMinY(firstRect);
    }

    return value;
}

这种方法也会切断列分隔符,而另一种方法则让它们绘制到表格底部。 - Jim75

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接