在NSView中创建网格

时间:2010-04-26 22:27:49

标签: objective-c cocoa grid nsview drawrect

我目前有一个NSView绘制网格图案(基本上是水平线和垂直线的指南),其想法是用户可以更改网格的间距和网格的颜色。

网格的目的是在排列对象时充当用户的指南。除了一个例外,一切正常。当我通过拖动调整大小手柄调整NSWindow的大小时,如果我的网格间距特别小(比如10像素)。阻力调整大小变得昏昏欲睡。

网格的drawRect代码如下:

-(void)drawRect:(NSRect)dirtyRect {

    NSRect thisViewSize = [self bounds];

    // Set the line color

    [[NSColor colorWithDeviceRed:0 
                           green:(255/255.0) 
                            blue:(255/255.0) 
                           alpha:1] set];

    // Draw the vertical lines first

    NSBezierPath * verticalLinePath = [NSBezierPath bezierPath];

    int gridWidth = thisViewSize.size.width;
    int gridHeight = thisViewSize.size.height;

    int i;

    while (i < gridWidth)
    {
        i = i + [self currentSpacing];

        NSPoint startPoint = {i,0};
        NSPoint endPoint = {i, gridHeight};

        [verticalLinePath setLineWidth:1];
        [verticalLinePath moveToPoint:startPoint];
        [verticalLinePath lineToPoint:endPoint];
        [verticalLinePath stroke];
    }

    // Draw the horizontal lines

    NSBezierPath * horizontalLinePath = [NSBezierPath bezierPath];

    i = 0;

    while (i < gridHeight)
    {
        i = i + [self currentSpacing];

        NSPoint startPoint = {0,i};
        NSPoint endPoint = {gridWidth, i};

        [horizontalLinePath setLineWidth:1];
        [horizontalLinePath moveToPoint:startPoint];
        [horizontalLinePath lineToPoint:endPoint];

        [horizontalLinePath stroke];
    }
}

我怀疑这完全与我绘制网格的方式有关,并愿意就如何更好地解决这个问题提出建议。

我可以看到效率低下的位置,拖动调整大小NSWindow在调整大小时不断调用此视图中的drawRect,网格越近,每个像素阻力越大父窗口。

我正在考虑将视图隐藏在窗口的大小调整上,但它感觉不那么动态。我希望用户体验非常流畅,没有任何明显的延迟或闪烁。

有没有人对更好或更有效的绘制网格方法有任何想法?

所有的帮助,一如既往,非常感谢。

3 个答案:

答案 0 :(得分:13)

您无意中在您的算法中引入了Schlemiel。每次在循环中调用moveToPointlineToPoint时,实际上是在同一路径中添加了更多行,每次调用{{1>时都会绘制所有在那条路上。

这意味着你第一次画一行,第二次画两行,第三次画三行等等......

快速解决方法是每次循环使用新路径只需在循环后执行stroke (感谢Jason Coco这个想法):

stroke

更新: 另一种方法是避免完全创建path = [NSBezierPath path]; while (...) { ... [path setLineWidth:1]; [path moveToPoint:startPoint]; [path lineToPoint:endPoint]; } [path stroke]; ,只使用strokeLineFromPoint:toPoint:类方法:

NSBezierPath

更新#2: 到目前为止,我对这些方法做了一些基本的基准测试。我正在使用一个800x600像素的窗口,十个像素的网格间距,我有可可重绘窗口一千次,从800x600扩展到900x700并再次返回。在我的2GHz Core Duo Intel MacBook上运行,我看到以下几次:

[NSBezierPath setDefaultLineWidth:1];
while (...)
{
    ...
    [NSBezierPath strokeLineFromPoint:startPoint toPoint:endPoint];
}

这意味着减速完全是由重复引起的,而且几项微观改进中的任何一项都没有做到实际加快速度。这不应该是一个惊喜,因为屏幕上像素的实际绘制(几乎总是)比简单的循环和数学运算更加处理器密集。

要吸取的教训:

  1. 隐藏的Schlemiels可以真的减慢事情。
  2. 始终对您的代码before doing unnecessary optimization
  3. 进行分析

答案 1 :(得分:0)

您应该运行Instruments Cpu Sampler来确定大部分时间花在哪里,然后根据该信息进行优化。如果是中风,将它放在循环外面。如果它正在绘制路径,请尝试将渲染卸载到gpu。看看CALayer是否可以提供帮助。

答案 2 :(得分:0)

也许这个派对迟到了,不过有人会觉得这很有帮助。最近,我需要为客户定制组件,以便重新创建网格可调整大小的叠加UIView。以下应该是工作,即使是很小的尺寸也没有问题。

代码适用于iPhone(UIView),但可以非常快速地移植到NSView。

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextClearRect(context, rect);
    CGContextSetStrokeColorWithColor(context, [UIColor whiteColor].CGColor);

    //corners
    CGContextSetLineWidth(context, 5.0);
    CGContextMoveToPoint(context, 0, 0);
    CGContextAddLineToPoint(context, 15, 0);
    CGContextMoveToPoint(context, 0, 0);
    CGContextAddLineToPoint(context, 0, 15);
    CGContextMoveToPoint(context, rect.size.width, 0);
    CGContextAddLineToPoint(context, rect.size.width-15, 0);
    CGContextMoveToPoint(context, rect.size.width, 0);
    CGContextAddLineToPoint(context, rect.size.width, 15);
    CGContextMoveToPoint(context, 0, rect.size.height);
    CGContextAddLineToPoint(context, 15, rect.size.height);
    CGContextMoveToPoint(context, 0, rect.size.height);
    CGContextAddLineToPoint(context, 0, rect.size.height-15);
    CGContextMoveToPoint(context, rect.size.width, rect.size.height);
    CGContextAddLineToPoint(context, rect.size.width-15, rect.size.height);
    CGContextMoveToPoint(context, rect.size.width, rect.size.height);
    CGContextAddLineToPoint(context, rect.size.width, rect.size.height-15);
    CGContextStrokePath(context);


    //border
    CGFloat correctRatio = 2.0;
    CGContextSetLineWidth(context, correctRatio);
    CGContextAddRect(context, rect);
    CGContextStrokePath(context);

    //grid
    CGContextSetLineWidth(context, 0.5);
    for (int i=0; i<4; i++) {
        //vertical
        CGPoint aPoint = CGPointMake(i*(rect.size.width/4), 0.0);
        CGContextMoveToPoint(context, aPoint.x, aPoint.y);
        CGContextAddLineToPoint(context,aPoint.x, rect.size.height);
        CGContextStrokePath(context);

        //horizontal
        aPoint = CGPointMake(0.0, i*(rect.size.height/4));
        CGContextMoveToPoint(context, aPoint.x, aPoint.y);
        CGContextAddLineToPoint(context,rect.size.width, aPoint.y);
        CGContextStrokePath(context);
    }

}