问题描述
我知道有关更改交替行颜色的其他问题。这很容易,这不是我想做的。
我想在基于视图的NSTableView中绘制自定义交替颜色的行, iTunes 11(在此行的顶部和底部有轻微的边框,如下图所示):
注意:
可以子类化NSTableRowView并做我的自定义绘图。但是,这不是一个可以接受的答案,因为自定义行将只用于表中有数据的行。换句话说,如果表只有5行,这5行将使用我的自定义NSTableRowView类,但是剩余的表中的行(它们是空的)将使用标准的交替颜色。在这种情况下,前5行将显示挡板,其余的将不会。不好。
那么,我如何使用NSTableView来绘制这些交替的填充行和空白行呢?
轻微的表圈,就像你所说的,实际上可以轻易地做一些作弊。因为,如果你仔细看,每个单元格的顶部是一个比浅黑的交替行略微浅蓝色,每个单元格的底部是一个暗灰色的颜色,你可以继承NSTableView,然后覆盖 - (void)drawRow:(NSInteger)row clipRect:(NSRect)clipRect
:
)drawRow:(NSInteger)row clipRect:(NSRect)clipRect
{
//使用http://stackoverflow.com/a/5101923/945847的绘图代码,但将颜色更改为
//看起来像iTunes的交替行。
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);
/ *略深灰色* /
[[NSColor colorWithCalibratedWhite:0.912 alpha:1.000] set];
/ *获取当前图形上下文* /
CGContextRef currentContext = [[NSGraphicsContext currentContext] graphicsPort];
/ *绘制稍微浅蓝色的一个像素行* /
CGContextSetLineWidth(currentContext,1.0f);
/ *开始在我们的单元格顶部的行* /
CGContextMoveToPoint(currentContext,0.0f,NSMaxY(cellBounds));
/ *结束在我们的tableview的边缘,对于多列,这实际上是overkill * /
CGContextAddLineToPoint(currentContext,NSMaxX(cellBounds),NSMaxY(cellBounds));
/ *使用上下文的当前颜色绘制线* /
CGContextStrokePath(currentContext);
/ *浅蓝色* /
[[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];
}
当在一个快速的表视图中完成时,它看起来像这样:
但该怎么办关于tableview的顶部和底部?毕竟,他们仍然是一个丑陋的白色,或默认交替行的颜色。好吧,正如苹果透露的(有趣的是,有趣的是,。
I am aware that there are other questions on SO about changing alternating row colors. That's easy and it's not what I want to do.
I want to draw custom alternating-colored rows in a view-based NSTableView that look like those from iTunes 11 (slight bezel at the top and bottom of the row, as shown in this screenshot):
NOTE:
I know I can subclass NSTableRowView and do my custom drawing there. However, this is NOT an acceptable answer because the custom row will only be used for rows that have data in the table. In other words, if the table has only 5 rows, those 5 rows will use my custom NSTableRowView class but the remaining "rows" in the rest of the table (which are empty) will use the standard alternating colors. In that case, the first 5 rows will show the bezel and the remaining ones won't. Not good.
So, how can I hack NSTableView to draw these styled alternating rows for both filled and empty rows?
That "slight bezel", as you put it, can actually be easily done with a little cheating on our part. Because, if you look closely, the top of every cell is a slightly lighter blue color than the dark alternating row, and the bottom of every cell is a dark grayish color, you can subclass NSTableView, then override - (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];
}
Which, when done in a quick little tableview, looks like this:
But what to do about the top and bottom of the tableview? After all, they'll still be either an ugly white, or the default alternating rows color. Well, as Apple revealed (in a talk titled, interestingly enough View Based NSTableView, Basic To Advanced), you can override -(void)drawBackgroundInClipRect:(NSRect)clipRect
and do a little math to draw the background of the tableview like extra rows. A quick implementation looks something like this:
-(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--;
}
}
}
But then, this leaves the bottom of the tableview that same ugly blank white color! So, we have to also override -(void)drawGridInClipRect:(NSRect)clipRect
. Yet another quick implementation looks like this:
-(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++;
}
}
When all is said and done, we get nice little fake tableview cell rows on the top and bottom of our clipview that looks a little like this:
The full subclass can be found here.
这篇关于绘制NSTableView交替行像iTunes 11的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!