Cocoa NSImageView始终显示在任何其他类型视图的顶部

Cocoa NSImageView始终显示在任何其他类型视图的顶部,cocoa,appkit,Cocoa,Appkit,我正在开发一个数据库应用程序,它有一个与Interface Builder非常相似的图形编辑器。然而,与IB不同的是,该编辑器可以从编辑器模式切换到实时模式,其中用户界面是完全可操作的(可以单击按钮、编辑文本等)。为此,图形编辑器使用标准的Appkit接口类——NSButton、NSTextView等。编辑器本身是使用NSView的自定义子类实现的。所有用户界面元素都是此自定义NSView的子视图,使用addSubview:方法添加新元素,使新元素成为最上面可见的元素(注意--视图不是层备份的,

我正在开发一个数据库应用程序,它有一个与Interface Builder非常相似的图形编辑器。然而,与IB不同的是,该编辑器可以从编辑器模式切换到实时模式,其中用户界面是完全可操作的(可以单击按钮、编辑文本等)。为此,图形编辑器使用标准的Appkit接口类——NSButton、NSTextView等。编辑器本身是使用NSView的自定义子类实现的。所有用户界面元素都是此自定义NSView的子视图,使用
addSubview:
方法添加新元素,使新元素成为最上面可见的元素(注意--视图不是层备份的,只是常规视图)。用户还可以使用“前置”和“后置”命令更改子视图的顺序。这部电影展示了两个重叠的NSButton元素(出于演示目的,当然通常不会重叠),以及程序如何重新排列子视图以更改用户界面元素的Z顺序

问题是,这适用于除NSImageView之外的所有类型的接口元素。在电影之前有两个元素,一个NSButton和一个NSImageView。NSButton实际上一直处于“顶部”,NSImageView元素应该显示在按钮后面,但无论子视图的顺序如何,NSImageView始终显示在顶部

如果有两个重叠的NSImageView对象,则它们之间的可见堆叠顺序是不可预测的,但无论子视图的顺序如何,它们都将始终显示在所有其他对象之上

一个可能有用的线索是,如果我实现自己的自定义视图,直接在它的
drawRect:
方法中绘制图像,效果会很好。所以这是一个可能的解决方案,但我不愿意,因为这意味着重新实现NSImageView通常会处理的大量有用功能,其中一些功能相当复杂,比如支持动画GIF显示。除了这个分层/z顺序问题外,NSImageView的其他所有功能都可以正常工作

也许NSImageView在没有我要求的情况下使用了层背衬,所以它没有与我的其他对象正确混合?我找不到任何文件表明这一点。我不是在链接QuartzCore框架

下面是将NSImageView元素作为子视图添加到图形编辑器视图的代码

- (void)objectDidAppearBelow:(NSView *)nextView
{
    FormView * formView = [FormWindowController currentFormView]; // get view element will be placed into
    NSScrollView * imageContainer = [[NSScrollView alloc] initWithFrame:insideBorderRect];
    ImageView * ixView = [[ImageView alloc] initWithFrame:[self insideFormObjectBorder:objectRectangle]];
    [ixView setOwnerObject:self];
    [imageContainer setDocumentView:ixView];    
    [imageContainer setAutoresizesSubviews:YES];
    [shapeView addSubview:imageContainer Below:nextView];
    imageDocumentView = ixView; // save weak reference to image view so it can be manipulated
}
在其他地方,NSButton(按钮、单选按钮等有几种变体)、NSTextView、NSTableView(列表和矩阵)、NSSlider、NSScroller、NSSegmentedControl甚至WebView的代码几乎相同。所有其他对象都可以正确处理重叠对象,包括WebView,只有NSImageView不能按预期工作


作为我的参考,这是《全景X问题追踪》中的#429。

我在2018年WWDC上与苹果工程师讨论了这个问题。事实证明,正如我所怀疑的,在某些情况下,即使您没有要求,Appkit也会为NSImageView使用层备份!因此,最好的解决方案是将所有视图切换到图层备份(这将在Mojave中自动发生)

在这个特殊的例子中,NSImageView位于NSScrollView中,我没有提到它,因为我认为它不重要(我的错)。事实证明,在这种情况下,Appkit认为使用层备份将是一个好主意(优化滚动)。因此,另一种解决方法是将NSImageView子类化(我出于其他原因已经这样做了),并添加此方法(由一位苹果工程师当场编写,他更愿意保持未经认证)

+(BOOL)与响应滚动兼容{

如果(NSAppKitVersionNumber),您能告诉我们如何创建一个小项目来演示这个问题吗?
+ (BOOL)isCompatibleWithResponsiveScrolling {
    if (NSAppKitVersionNumber <= 1561. /* NSAppKitVersionNumber10_13 */) {
        return NO;
    } else {
        return YES;
    }
}