9

交互の行の色を変更することについて、SO に他の質問があることを認識しています。それは簡単で、私がやりたいことではありません。

ビューベースの NSTableView で、iTunes 11 のようなカスタムの交互に色分けされた行を描画したいと考えています (このスクリーンショットに示すように、行の上部と下部にあるわずかなベゼル):

iTunes 11 のスクリーンショット

ノート:

NSTableRowView をサブクラス化し、そこでカスタム描画を行うことができることを知っています。ただし、カスタム行はテーブルにデータがある行にのみ使用されるため、これは受け入れられる答えではありません。つまり、テーブルに 5 行しかない場合、これらの 5 行はカスタム NSTableRowView クラスを使用しますが、テーブルの残りの (空の) 残りの「行」は標準の交互の色を使用します。その場合、最初の 5 行にはベゼルが表示され、残りの行には表示されません。良くない。

では、NSTableView をハックして、塗りつぶされた行と空の行の両方に対してこれらのスタイル化された交互の行を描画するにはどうすればよいでしょうか?

4

3 に答える 3

15

その「わずかなベゼル」は、あなたが言うように、私たちの側で少しごまかすことで実際に簡単に行うことができます。よく見ると、すべてのセルの上部は暗い交互の行よりもわずかに明るい青色であり、すべてのセルの下部は暗い灰色がかった色であるため、NSTableViewをサブクラス化してから、オーバーライドできます- (void)drawRow:(NSInteger)row clipRect:(NSRect)clipRect

- (void)drawRow:(NSInteger)row clipRect:(NSRect)clipRect
{
    //Use the drawing code from http://stackoverflow.com/a/5101923/945847, but change the colors to
    //look like iTunes's alternating rows.
    NSRect cellBounds = [self rectOfRow:row];
    NSColor *color = (row % 2) ? [NSColor colorWithCalibratedWhite:0.975 alpha:1.000] : [NSColor colorWithCalibratedRed:0.932 green:0.946 blue:0.960 alpha:1.000];
    [color setFill];
    NSRectFill(cellBounds);

    /* Slightly dark gray color */
    [[NSColor colorWithCalibratedWhite:0.912 alpha:1.000] set];
    /* Get the current graphics context */
    CGContextRef currentContext = [[NSGraphicsContext currentContext]graphicsPort];
    /*Draw a one pixel line of the slightly lighter blue color */
    CGContextSetLineWidth(currentContext,1.0f);
    /* Start the line at the top of our cell*/
    CGContextMoveToPoint(currentContext,0.0f, NSMaxY(cellBounds));
    /* End the line at the edge of our tableview, for multi-columns, this will actually be overkill*/
    CGContextAddLineToPoint(currentContext,NSMaxX(cellBounds), NSMaxY(cellBounds));
    /* Use the context's current color to draw the line */
    CGContextStrokePath(currentContext);

    /* Slightly lighter blue color */
    [[NSColor colorWithCalibratedRed:0.961 green:0.970 blue:0.985 alpha:1.000] set];
    CGContextSetLineWidth(currentContext,1.0f);
    CGContextMoveToPoint(currentContext,0.0f,1.0f);
    CGContextAddLineToPoint(currentContext,NSMaxX(self.bounds), 1.0f);
    CGContextStrokePath(currentContext);

    [super drawRow:row clipRect:clipRect];
}

これは、簡単なテーブルビューで実行すると、次のようになります。 素敵なベゼル!

しかし、テーブルビューの上部と下部についてはどうすればよいでしょうか。結局のところ、それらはまだ醜い白か、デフォルトの交互の行の色のいずれかです。さて、Appleが明らかにしたように(興味深いことに、View Based NSTableView、Basic To Advancedというタイトルの講演で)、余分な行のようにテーブルビューの背景を描画するためにオーバーライド-(void)drawBackgroundInClipRect:(NSRect)clipRectして少し計算することができます。簡単な実装は次のようになります。

-(void)drawBackgroundInClipRect:(NSRect)clipRect
{
    // The super class implementation obviously does something more
    // than just drawing the striped background, because
    // if you leave this out it looks funny
    [super drawBackgroundInClipRect:clipRect];

    CGFloat   yStart   = 0;
    NSInteger rowIndex = -1;

    if (clipRect.origin.y < 0) {
        while (yStart > NSMinY(clipRect)) {
            CGFloat yRowTop = yStart - self.rowHeight;

            NSRect rowFrame = NSMakeRect(0, yRowTop, clipRect.size.width, self.rowHeight);
            NSUInteger colorIndex = rowIndex % self.colors.count;
            NSColor *color = [self.colors objectAtIndex:colorIndex];
            [color set];
            NSRectFill(rowFrame);

            /* Slightly dark gray color */
            [[NSColor colorWithCalibratedWhite:0.912 alpha:1.000] set];
            /* Get the current graphics context */
            CGContextRef currentContext = [[NSGraphicsContext currentContext]graphicsPort];
            /*Draw a one pixel line of the slightly lighter blue color */
            CGContextSetLineWidth(currentContext,1.0f);
            /* Start the line at the top of our cell*/
            CGContextMoveToPoint(currentContext,0.0f, yRowTop + self.rowHeight - 1);
            /* End the line at the edge of our tableview, for multi-columns, this will actually be overkill*/
            CGContextAddLineToPoint(currentContext,NSMaxX(clipRect), yRowTop + self.rowHeight - 1);
            /* Use the context's current color to draw the line */
            CGContextStrokePath(currentContext);

            /* Slightly lighter blue color */
            [[NSColor colorWithCalibratedRed:0.961 green:0.970 blue:0.985 alpha:1.000] set];
            CGContextSetLineWidth(currentContext,1.0f);
            CGContextMoveToPoint(currentContext,0.0f,yRowTop);
            CGContextAddLineToPoint(currentContext,NSMaxX(clipRect), yRowTop);
            CGContextStrokePath(currentContext);

            yStart -= self.rowHeight;
            rowIndex--;
        }
    }
}

しかし、これにより、テーブルビューの下部に同じ醜い空白の白い色が残ります!したがって、もオーバーライドする必要があります-(void)drawGridInClipRect:(NSRect)clipRect。さらに別の簡単な実装は次のようになります。

-(void)drawGridInClipRect:(NSRect)clipRect {
    [super drawGridInClipRect:clipRect];

    NSUInteger numberOfRows = self.numberOfRows;
    CGFloat yStart = 0;
    if (numberOfRows > 0) {
        yStart = NSMaxY([self rectOfRow:numberOfRows - 1]);
    }
    NSInteger rowIndex = numberOfRows + 1;

    while (yStart < NSMaxY(clipRect)) {
        CGFloat yRowTop = yStart - self.rowHeight;

        NSRect rowFrame = NSMakeRect(0, yRowTop, clipRect.size.width, self.rowHeight);
        NSUInteger colorIndex = rowIndex % self.colors.count;
        NSColor *color = [self.colors objectAtIndex:colorIndex];
        [color set];
        NSRectFill(rowFrame);

        /* Slightly dark gray color */
        [[NSColor colorWithCalibratedWhite:0.912 alpha:1.000] set];
        /* Get the current graphics context */
        CGContextRef currentContext = [[NSGraphicsContext currentContext]graphicsPort];
        /*Draw a one pixel line of the slightly lighter blue color */
        CGContextSetLineWidth(currentContext,1.0f);
        /* Start the line at the top of our cell*/
        CGContextMoveToPoint(currentContext,0.0f, yRowTop - self.rowHeight);
        /* End the line at the edge of our tableview, for multi-columns, this will actually be overkill*/
        CGContextAddLineToPoint(currentContext,NSMaxX(clipRect), yRowTop - self.rowHeight);
        /* Use the context's current color to draw the line */
        CGContextStrokePath(currentContext);

        /* Slightly lighter blue color */
        [[NSColor colorWithCalibratedRed:0.961 green:0.970 blue:0.985 alpha:1.000] set];
        CGContextSetLineWidth(currentContext,1.0f);
        CGContextMoveToPoint(currentContext,0.0f,yRowTop);
        CGContextAddLineToPoint(currentContext,NSMaxX(self.bounds), yRowTop);
        CGContextStrokePath(currentContext);

        yStart += self.rowHeight;
        rowIndex++;
    }
}

すべてのことを言い終えると、クリップビューの上部と下部に、次のように見える小さな偽のテーブルビューセル行が表示されます。

ここに画像の説明を入力してください

完全なサブクラスはここにあります。

于 2013-01-06T21:34:44.857 に答える
1

あなたが使用することができます

- (void)setUsesAlternatingRowBackgroundColors:(BOOL)useAlternatingRowColors

背景に標準の交互の行の色を指定するにはuseAlternatingRowColorsYES を指定し、無地の色を指定するには NO を指定します。

于 2014-01-21T02:12:27.683 に答える
0

上部と下部の両方を描画できることがわかりましたdrawBackgroundInClipRect-本質的にelse@CodaFiのソリューションの欠落している節にあります。

backgroundColorしたがって、 andにアクセスできると仮定した場合の Swift でのアプローチは次のalternateBackgroundColorとおりです。

override func drawBackground(inClipRect clipRect: NSRect) {

    // I didn't find leaving this out changed appearance at all unlike
    // CodaFi stated.
    super.drawBackground(inClipRect: clipRect)

    guard usesAlternatingRowBackgroundColors else { return }

    drawTopAlternatingBackground(inClipRect: clipRect)
    drawBottomAlternatingBackground(inClipRect: clipRect)
}

fileprivate func drawTopAlternatingBackground(inClipRect clipRect: NSRect) {

    guard clipRect.origin.y < 0 else { return }

    let backgroundColor = self.backgroundColor
    let alternateColor = self.alternateBackgroundColor

    let rectHeight = rowHeight + intercellSpacing.height
    let minY = NSMinY(clipRect)
    var row = 0

    while true {

        if row % 2 == 0 {
            backgroundColor.setFill()
        } else {
            alternateColor.setFill()
        }

        let rowRect = NSRect(
            x: 0,
            y: (rectHeight * CGFloat(row) - rectHeight),
            width: NSMaxX(clipRect),
            height: rectHeight)
        NSRectFill(rowRect)
        drawBezel(inRect: rowRect)

        if rowRect.origin.y < minY { break }

        row -= 1
    }
}

fileprivate func drawBottomAlternatingBackground(inClipRect clipRect: NSRect) {

    let backgroundColor = self.backgroundColor
    let alternateColor = self.alternateBackgroundColor

    let rectHeight = rowHeight + intercellSpacing.height
    let maxY = NSMaxY(clipRect)
    var row = rows(in: clipRect).location

    while true {

        if row % 2 == 1 {
            backgroundColor.setFill()
        } else {
            alternateColor.setFill()
        }

        let rowRect = NSRect(
            x: 0,
            y: (rectHeight * CGFloat(row)),
            width: NSMaxX(clipRect),
            height: rectHeight)
        NSRectFill(rowRect)
        drawBezel(inRect: rowRect)

        if rowRect.origin.y > maxY { break }

        row += 1
    }
}

func drawBezel(inRect rect: NSRect) {

    let topLine = NSRect(x: 0, y: NSMaxY(rect) - 1, width: NSWidth(rect), height: 1)
    NSColor(calibratedWhite: 0.912, alpha: 1).set()
    NSRectFill(topLine)

    let bottomLine = NSRect(x: 0, y: NSMinY(rect)   , width: NSWidth(rect), height: 1)
    NSColor(calibratedRed:0.961, green:0.970, blue:0.985, alpha:1).set()
    NSRectFill(bottomLine)
}

NSTableRowViewサブクラスを描画しない場合:

override func drawRow(_ row: Int, clipRect: NSRect) {

    let rowRect = rect(ofRow: row)
    let color = row % 2 == 0 ? self.backgroundColor : self.alternateBackgroundColor
    color.setFill()
    NSRectFill(rowRect)
    drawBezel(inRect: rowRect)
}
于 2017-05-02T07:06:20.257 に答える