Создание сетки в NSView - PullRequest
       28

Создание сетки в NSView

6 голосов
/ 27 апреля 2010

В настоящее время у меня есть 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 ]

13 голосов
/ 27 апреля 2010

Вы случайно ввели Schlemiel в свой алгоритм. Каждый раз, когда вы вызываете moveToPoint и lineToPoint в своих циклах, вы фактически добавляете больше линий к одному и тому же пути, все из которых будут отображаться каждый раз, когда вы вызываете stroke на этом пути.

Это означает, что вы рисуете одну линию в первый раз, две линии во второй раз, три линии в третий раз и т. Д ...

Быстрым решением было бы использовать новый путь каждый раз через цикл просто выполнить stroke после цикла (спасибо Джейсону Коко за идею):

path = [NSBezierPath path];
while (...)
{
    ...

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

Обновление: Другой подход заключается в том, чтобы вообще не создавать его NSBezierPath, а просто использовать метод класса strokeLineFromPoint: toPoint: :

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

Обновление № 2: До сих пор я провел некоторые базовые тесты на подходах. Я использую окно размером 800x600 пикселей, интервал сетки в десять пикселей, и мне приходится какао перерисовывать окно тысячу раз, масштабируя от 800x600 до 900x700 и обратно. Работая на моем 2 ГГц Core Duo Intel MacBook, я вижу следующее время:

Original method posted in question:  206.53 seconds  
Calling stroke after the loops:       16.68 seconds  
New path each time through the loop:  16.68 seconds  
Using strokeLineFromPoint:toPoint:    16.68 seconds  

Это означает, что замедление было полностью вызвано повторением, и что любое из нескольких микро-улучшений мало что делает для ускорения. Это не должно вызывать удивления, так как фактическое рисование пикселей на экране (почти всегда) требует гораздо больше ресурсов процессора, чем простые циклы и математические операции.

Извлеченные уроки:

  1. Скрытые шлемилы могут действительно замедлять ход событий.
  2. Всегда профилируйте свой код перед выполнением ненужной оптимизации
0 голосов
/ 07 января 2016

Может быть, опоздать на вечеринку, однако кто-то может найти это полезным.Недавно мне потребовались пользовательские компоненты для заказчика, чтобы воссоздать сетку с изменяемым размером наложения 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);
    }

}
0 голосов
/ 27 апреля 2010

Вам следует запустить Instruments Cpu Sampler, чтобы определить, где большую часть времени тратится, а затем оптимизировать на основе этой информации.Если это удар, поместите его вне цикла.Если он рисует путь, попробуйте разгрузить рендеринг в GPU.Посмотрите, может ли CALayer помочь.

...