我可以使用 CALayer 来加速视图渲染吗?

发布于 2025-01-07 16:53:18 字数 212 浏览 0 评论 0原文

我正在制作一个自定义 NSView 对象,其中一些内容经常更改,而另一些内容则很少更改。事实证明,变化较少的部分需要花费最多的时间来绘制。我想做的是将这两个部分呈现在不同的层中,以便我可以分别更新其中一个或另一个,从而使我的用户免受缓慢的用户界面的困扰。

我该怎么做呢?我还没有找到很多关于此类事情的好的教程,也没有一个讨论在 CALayer 上渲染 NSBezierPaths。有人有想法吗?

I am making a custom NSView object that has some content that changes often, and some that changes much more infrequently. As it would turn out, the parts that change less often take the most time to draw. What I would like to do is render these two parts in different layers, so that I can update one or the other separately, thus sparing my user a sluggish user interface.

How might I go about doing this? I have not found many good tutorials on this sort of thing, and none that talk about rendering NSBezierPaths on a CALayer. Ideas anyone?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

总攻大人 2025-01-14 16:53:18

您的预感是正确的,这实际上是优化绘图的绝佳方法。我自己做过,我有一些大型静态背景,我想避免当元素移动到顶部时重新绘制。

您所需要做的就是为视图中的每个内容项添加 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]。然后,这将调用上面的委托方法来提供图层的更新内容。

请注意,默认情况下,当您更改图层内容时,核心动画会为新内容提供良好的淡入淡出过渡。但是,如果您自己处理绘图,您可能不希望这样做,因此为了防止图层内容更改时动画默认淡出,您还必须实现 actionForLayer:forKey: 委托方法并通过返回 null 操作来阻止动画:

- (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;
}

Your hunch is right, this is actually an excellent way to optimise drawing. I've done it myself where I had some large static backgrounds that I wanted to avoid redrawing when elements moved on top.

All you need to do is add CALayer objects for each of the content items in your view. To draw the layers, you should set your view as the delegate for each layer and then implement the drawLayer:inContext: method.

In that method you just draw the content of each layer:

- (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
}

When you want to update the content of one of the layers, just call [yourLayer setNeedsDisplay]. This will then call the delegate method above to provide the updated content of the layer.

Note that by default, when you change the layer content, Core Animation provides a nice fade transition for the new content. However, if you're handling the drawing yourself you probably don't want this, so in order to prevent the default fade in animation when the layer content changes, you also have to implement the actionForLayer:forKey: delegate method and prevent the animation by returning a null action:

- (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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文