NSImageView始终显示在任何其他类型的视图之上

时间:2018-05-28 08:19:04

标签: cocoa appkit

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

Bring-to-Front, Send-to-Back

问题是,这适用于除 NSImageView之外的各种界面元素。在电影之前有两个元素,一个是NSButton和一个NSImageView。 NSButton实际上一直是“在顶部”,NSImageView元素应该出现在按钮后面,但无论子视图的顺序如何,NSImageView总是出现在顶部。

NSImageView always on top

如果有两个重叠的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无法按预期工作。

对于我的参考,这是Panorama X问题跟踪器中的#429。

1 个答案:

答案 0 :(得分:2)

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

在这种特殊情况下,NSImageView位于NSScrollView内,我没有提及,因为我认为它不重要(我很糟糕)。事实证明,在这种情况下,Appkit认为使用图层支持将是一个好主意(以优化滚动)。因此,解决此问题的另一种方法是将NSImageView子类化(出于其他原因我已经做了)并添加此方法(由更愿意保持信誉的Apple工程师当场编写)。

+ (BOOL)isCompatibleWithResponsiveScrolling {
    if (NSAppKitVersionNumber <= 1561. /* NSAppKitVersionNumber10_13 */) {
        return NO;
    } else {
        return YES;
    }
}

尽管文件很少,但我确信这是公开的,已记录的API的全部(令人惊讶)。 What's new in OS X 10.9发行说明中对响应式滚动进行了一些讨论。使用NSAppKitVersionNumber进行检查是为了确保在Mojave上运行时关闭此修补程序,因为所有内容都支持图层。