【问题标题】:Calling setNeedsDisplay:YES on layer-hosting view does not redraw the view在图层托管视图上调用 setNeedsDisplay:YES 不会重绘视图
【发布时间】:2011-04-08 13:19:29
【问题描述】:

我在自定义NSView 子类中设置了这样的层托管视图:

[self setLayer:rootLayer];
[self setWantsLayer:YES];

在每个子层上调用setNeedsDisplay 后,我将所有子层添加到层树中。每个层的内容由我层的代理的drawLayer:inContext 方法提供。

这是我的问题:

初始化我的视图后,视图会正确绘制。但是,当模型发生更改并且我从视图控制器调用 [myCustomView setNeedsDisplay:YES]; 时,不会调用 drawLayer:inContext

我现在很困惑如何更新视图:

  • 是否必须在层树中的每个 CALayer 上调用 setNeedsDisplay 方法?
  • 不应该在 layer-hosting view 上调用 setNeedsDisplay:YES 本身触发整个 layer 树的重绘吗?

感谢您的帮助。

编辑

我在 NSView 类参考中找到了一些东西

layer-backed view 是由 Core Animation 层支持的视图。视图完成的任何绘图都缓存在支持层中。您只需调用 setWantsLayer: 值 YES 即可配置一个支持层的视图。视图类会自动为您创建一个支持层,并且您使用视图类的绘图机制。使用 layer-backed 视图时,您永远不应该直接与 layer 交互。

layer-hosting view 是一个包含你打算直接操作的 Core Animation 层的视图。您可以通过实例化 Core Animation 图层类的实例并使用视图的 setLayer: 方法设置该图层来创建图层托管视图。完成此操作后,然后调用 setWantsLayer: 值为 YES。使用 layer-hosting view 时,您不应依赖视图进行绘图,也不应将子视图添加到 layer-hosting view。

link to documentation

就我而言,我有一个图层托管视图。那么这是否确实意味着我必须手动触发重绘?我应该在自定义 NSView 中实现一个伪 drawRect 方法以在更改的 CALayers 上调用适当的 setNeedsDisplay 吗?

【问题讨论】:

    标签: cocoa core-animation


    【解决方案1】:

    在 Apple 的 sample code 对 kiosk 样式菜单的进一步研究后,我发现如果您使用的是 layer-hosting 视图,您必须自己处理由于模型更改而需要的屏幕更新。在 NSView 上调用 setNeedsDisplay:YES 不会做任何事情。

    因此,如果必须更新视图,则必须编写一个类似 reloadData 的方法,并在其中调用 setNeedsDisplay 在每个需要刷新的 CALayer 上。我仍然不确定在根层上调用此方法是否会传播到所有子层,但我不这么认为。

    我现在通过在需要重新缓存的各个 CALayer 上调用 setNeedsDisplay 解决了这个问题。它可以正常工作。

    【讨论】:

    【解决方案2】:

    还有一个常用的做法是使用一个空的“drawrect”,例如 -(void) drawRect:(NSRect)dirtyRect {} 来帮助强制进行绘图,我相信通过良好的 ole view.needsDisplay = YES;

    并且应该注意.. 确实发生的事情是 - 通过说你的NSView *view;layer.delegate = view; 会导致在调用[layer setNeedsDisplay]; 时绘制图层.. 通过- (void) drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {...}..

    沿着同样的思路...当说layer.layoutManager = view...随后要求[layer setNeedsLayout]; 将仅在实现- (void) layoutSublayersOfLayer:(CALayer *)layer {..} 方法时才会满足..

    这些重要的概念在 Apple 的文档中被掩饰/散布在各处......它们对于让任何事情都能正常工作非常关键。

    【讨论】:

    • 你能解释一下对初学者更友好的方式吗?
    • @carbonr 我知道这很令人困惑。但我说的很简单。如果事情没有正确显示,并且您的视图是该层的代表。您需要实现drawLayer:inContext,并且可能有一个空的drawRect: 并调用setNeedsDisplay。如果必须阅读,请阅读 10 遍,但信息就在那里。如果我没记错的话,就是这样。 ;)
    【解决方案3】:

    您可以通过更改视图的重绘策略来自动委托setNeedsDispay:。您必须将NSViewLayerContentsRedrawOnSetNeedsDisplay 分配给属性layerContentsRedrawPolicy(请参阅https://developer.apple.com/documentation/appkit/nsview/1483514-layercontentsredrawpolicy)。当您将setNeedsDisplay: 发送到视图时,这将触发图层的重绘:

    [self setLayer:rootLayer];
    [self setWantsLayer:YES];
    self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawOnSetNeedsDisplay;
    

    或在 Swift 中:

    layer = rootLayer
    wantsLayer = true
    layerContentsRedrawPolicy = .onSetNeedsDisplay
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多