【问题标题】:Layer hosting NSView within NSOutlineView在 NSOutlineView 中托管 NSView 的层
【发布时间】:2015-05-20 11:31:00
【问题描述】:

我正在尝试创建一个自定义 NSView,它承载 CALayer 层次结构以执行高效显示。然后这个NSView 被嵌入到一个NSTableCellView 中,该NSTableCellView 由基于视图的NSOutlineView 显示。

问题是每当我展开或折叠一个项目时,所有行都会被移动,但图层的内容仍然显示在更改轮廓之前的位置。

滚动NSOutlineView 似乎会刷新图层,并且此时它们会与它们的行重新同步。

我已经使用 Instruments 调试了这种行为,似乎滚动会引发布局操作,该操作会使用展开或折叠项目时应该发生的 setPosition: 调用更新图层。

这里是托管NSView 子类的简单层的一些示例代码。

@interface TestView : NSView

@end

@implementation TestView

- (instancetype)initWithFrame:(NSRect)frameRect
{
    self = [super initWithFrame:frameRect];
    CAShapeLayer* layer = [CAShapeLayer layer];
    layer.bounds = self.bounds;
    layer.position = CGPointMake(NSMidX(self.bounds), NSMidY(self.bounds));
    layer.path = [NSBezierPath bezierPathWithOvalInRect:self.bounds].CGPath;
    layer.fillColor = [NSColor redColor].CGColor;
    layer.delegate = self;
    self.layer = layer;
    self.wantsLayer = YES;
    return self;
}

@end

我已经尝试了很多潜在的解决方案来解决这个问题,但我找不到任何有趣的方法可以在 NSView 实例上调用,可以重写以调用 [self.layer setNeedsDisplay][self.layer setNeedsLayout]。我还在CALayer 本身上尝试了各种设置器,例如:

layer.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
layer.needsDisplayOnBoundsChange = YES;
self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawOnSetNeedsDisplay;

谁能帮我弄清楚如何让这个图层在NSOutlineView 中正确显示?

【问题讨论】:

    标签: objective-c macos cocoa calayer nsoutlineview


    【解决方案1】:

    您不必为任何祖先视图(如大纲视图)启用图层支持。

    根据我的经验,立即分配给视图的层(与子层相反)不需要设置其边界、位置或自动调整大小的掩码。它会自动跟踪视图的边界。事实上,我会避免设置这些属性,以防会破坏与视图边界 rect 的自动同步。

    所以,问题是:您如何安排视图随其父视图移动或调整大小?你在使用自动布局吗?如果是这样,您是否关闭了它的translatesAutoresizingMaskIntoConstraints?如果两者都是,您在视图上设置了什么约束?如果两者都不是,您是如何在其父视图中定位视图的?你设置的什么框架?此外,超级视图是否配置为自动调整其子视图的大小(可能是的,因为这是默认设置)?你对autoresizingMask的看法是什么?

    您还可以在自定义视图类中覆盖-setFrameOrigin:-setFrameSize: 并调用super。此外,添加日志记录以显示发生这种情况的时间以及新的框架矩形是什么。展开或折叠行时,视图是否按预期移动?

    【讨论】:

    • 好吧,我为 TestView 展示的代码是完整的。这是我添加了 TestView 的 Apple 的 OutlineView 演示项目的链接。该项目工作正常,直到您从 Application.xib 中的 Window 内容视图中删除该层。 filebin.ca/1zm0kIYSuMNo/OutlineView.zip
    【解决方案2】:

    我最终回答了我的问题。问题不在于我的 TestView 的实现方式。我只是错过了在应用程序中启用 CoreAnimation 支持的步骤之一。相关参考在核心动画编程指南中。

    基本上,在 iOS Core Animation 和 layer-backing 中总是默认启用的。在 OS X 上,它必须以这种方式启用:

    1. 链接到 QuartzCore 框架
    2. 通过执行以下操作之一为您的一个或多个 NSView 对象启用层支持
      • 在您的 nib 文件中,使用 View Effects 检查器为您的视图启用图层支持。检查器显示选定视图及其子视图的复选框。建议您尽可能在窗口的内容视图中启用图层支持
      • 对于您以编程方式创建的视图,请调用视图的 setWantsLayer: 方法并传递 YES 值以指示视图应使用图层。

    一旦我在任何 NSOutlineView 的父级上启用层支持,各种故障都将得到解决。

    【讨论】:

    • 谢谢你!我遇到了同样的问题(使用图层的自定义进度微调器)并且扩展/折叠单元格没有移动微调器。对我来说,我只需要在大纲视图上启用“图层支持”。在那之后,一切都解决了。
    • 很高兴这对您有用! :) 我知道我搜索这个答案已经有一段时间了!
    【解决方案3】:

    很难阅读NSOutlineView 参考文档并在此处找到可能适合您的单元重用信息。

    您可能看过outlineViewItemDidCollapse:,但它对我们的问题有点用处,因为它没有指向NSView 的指针,这是因为它比基于视图的大纲视图更老。

    也许有一个有用的提及,隐藏在NSOutlineViewDelegate 协议中,在基于视图的 NSOutlineView 方法部分中,there is a single mentionoutlineView:didRemoveRowView:forRow: 中:

    删除的 rowView 可能会被表格重用,因此此时应删除任何额外插入的视图。

    换句话说,当您调用大纲视图的makeViewWithIdentifier:owner: 时,对于具有特定 ID 的 cellView 或 rowView,您通常会得到一个循环视图。尤其是经常因为崩溃。顺便说一句,那个方法来自NSTableView超类,并且在那个引用中,还有this comment

    此方法还可能返回一个重复使用的视图,该视图具有相同的标识符,但屏幕上不再可用。如果无法从 nib 文件实例化或在重用队列中找到具有指定标识符的视图,则此方法返回 nil。

    因此,您可以选择更改 didRemoveRowView:forRow 中的视图层次结构或 niling 属性。但是,隐藏在第三个可可参考中,即NSView,在prepareForReusethis comment: 的评论中存在

    此方法提供了一种将视图重置为某些初始状态以便可以重复使用的方法。例如,NSTableView 类使用它来准备视图以供重用,从而避免在新视图滚动到视图时创建新视图的费用。如果您在自己的代码中实现视图重用系统,则可以在重用它们之前从您自己的代码中调用此方法。

    所以,TL;DR,您需要实现 prepareForReuse

    相关的引用(大部分)是NSOutlineViewNSTableCellView超类

    而且,FWIW,有一个similar question here,提问者似乎表明事情比我想象的还要糟糕,因为 NSOutlineView 在幕后比 NSTableView 更有创意。

    在我自己使用大纲视图和嵌入式 NSTextViews 的工作中,我看到了与展开/折叠/滚动相关的非常可怕的渲染打嗝,我似乎只在 NSOutlineViewDelegate 方法中进行了管理。在 iOS 上,他们帮大家将makeViewWithIdentifier 重命名为更明确的dequeueReusableCellViewWithIdentifier

    【讨论】:

    • 我已尝试按照您的建议实施prepareForReuse,但这还不足以解决问题。一些调试表明在处理比可见区域长的内容时调用了该方法。在另一种情况下,该方法根本从未使用过,这证明这不是解决方案。问题实际上在于 NSOutlineView 执行的显示和折叠动画没有正确处理子层。我还看到了您指出的另一个 SO 问题,但该问题尚未得到令人满意的回答。
    猜你喜欢
    • 2012-12-20
    • 2012-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多