Могу ли я использовать CALayer для ускорения рендеринга вида? - PullRequest
5 голосов
/ 21 февраля 2012

Я делаю собственный объект NSView, у которого есть контент, который часто меняется, а другой, который меняется гораздо реже.Как оказалось, детали, которые меняются реже, занимают больше времени для рисования.То, что я хотел бы сделать, это визуализировать эти две части в разных слоях, чтобы я мог обновлять одну или другую по отдельности, тем самым избавляя моего пользователя от медленного пользовательского интерфейса.

Как я могу поступить так?Я не нашел много хороших учебных пособий по этому виду вещей, и ни один из них не говорит о рендеринге NSBezierPaths на CALayer.Идеи кого-нибудь?

1 Ответ

4 голосов
/ 21 февраля 2012

Ваша догадка верна, на самом деле это отличный способ оптимизировать рисование. Я сделал это сам, где у меня были большие статические фоны, которые я хотел избежать, чтобы перерисовывать, когда элементы перемещались сверху.

Все, что вам нужно сделать, это добавить CALayer объектов для каждого элемента контента в вашем представлении. Чтобы нарисовать слои, вы должны установить свой вид в качестве делегата для каждого слоя, а затем реализовать метод drawLayer:inContext:.

В этом методе вы просто рисуете содержимое каждого слоя:

- (void)drawLayer:(CALayer*)layer inContext:(CGContextRef)ctx
{
    if(layer == yourBackgroundLayer)
    {   
        //draw your background content in the context
        //you can either use Quartz drawing directly in the CGContextRef,
        //or if you want to use the Cocoa drawing objects you can do this:
        NSGraphicsContext* drawingContext = [NSGraphicsContext graphicsContextWithGraphicsPort:ctx flipped:YES];
        NSGraphicsContext* previousContext = [NSGraphicsContext currentContext];
        [NSGraphicsContext setCurrentContext:drawingContext];
        [NSGraphicsContext saveGraphicsState];
        //draw some stuff with NSBezierPath etc
        [NSGraphicsContext restoreGraphicsState];
        [NSGraphicsContext setCurrentContext:previousContext];
    }
    else if (layer == someOtherLayer)
    {
        //draw other layer
    }
    //etc etc
}

Если вы хотите обновить содержимое одного из слоев, просто наберите [yourLayer setNeedsDisplay]. Затем будет вызван метод делегата, описанный выше, для предоставления обновленного содержимого слоя.

Обратите внимание, что по умолчанию, когда вы изменяете содержимое слоя, Core Animation обеспечивает приятный переход для нового контента. Однако, если вы обрабатываете рисунок самостоятельно, вы, вероятно, этого не хотите, поэтому, чтобы предотвратить затухание анимации по умолчанию при изменении содержимого слоя, вам также необходимо реализовать метод делегата actionForLayer:forKey: и предотвратить анимацию с помощью возврат нулевого действия:

- (id<CAAction>)actionForLayer:(CALayer*)layer forKey:(NSString*)key 
{
    if(layer == someLayer)
    {
        //we don't want to animate new content in and out
        if([key isEqualToString:@"contents"])
        {
            return (id<CAAction>)[NSNull null];
        }
    }
    //the default action for everything else
    return nil;
}
...