cocoa - NSImageView 总是显示在任何其他类型的视图之上
问题描述
我正在开发一个数据库应用程序,它具有与 Interface Builder 非常相似的图形编辑器。然而,与 IB 不同的是,这个编辑器可以从编辑器模式切换到实时模式,在这种模式下,用户界面是完全可操作的(可以单击按钮、编辑文本等)。为此,图形编辑器使用标准的 Appkit 接口类—— NSButton、NSTextView 等。编辑器本身是用 NSView 的自定义子类实现的。所有的用户界面元素都是这个自定义 NSView 的子视图,新元素是使用addSubview:
方法,使新元素成为最顶层的可见元素(注意——视图不是图层支持的,只是常规视图)。用户还可以使用Bring-to-Front 和Send-to-Back 命令来更改子视图的顺序。这部电影展示了两个重叠的 NSButton 元素(出于说明目的,当然通常您不会重叠它们),以及程序如何重新排列子视图以更改用户界面元素的 Z 顺序。
问题是,这适用于除NSImageView之外的所有类型的界面元素。在电影之前有两个元素,一个NSButton和一个NSImageView。NSButton 实际上一直在“顶部”,NSImageView 元素应该出现在按钮后面,但无论子视图的顺序如何,NSImageView 总是出现在顶部。
如果有两个重叠的 NSImageView 对象,它们之间的可见堆叠顺序是不可预知的,但无论子视图的顺序如何,它们总是会出现在所有其他对象之上。
一个可能有用的线索是,如果我实现自己的自定义视图,直接在它的drawRect:
方法中绘制图像,那效果很好。所以这是一种可能的解决方案,但我很不情愿,因为这意味着重新实现 NSImageView 通常负责的大量有用功能,其中一些非常复杂,例如支持动画 GIF 显示。除了这个分层/z-order 问题之外,关于 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。
解决方案
我在 WWDC 2018 上与 Apple 工程师讨论了这个问题。事实证明,正如我所怀疑的那样,在某些情况下,即使你没有要求 Appkit 也会为 NSImageView 使用层支持!所以最好的解决方案是将所有视图切换到图层支持(这将在 Mojave 中自动发生)。
在这种特殊情况下,NSImageView 位于 NSScrollView 中,我没有提到它,因为我认为它不重要(我的错)。事实证明,这就是 Appkit 认为使用层支持是一个好主意(优化滚动)的情况。所以解决这个问题的另一种方法是子类化 NSImageView(我已经因为其他原因做了)并添加这个方法(由一个喜欢保持不可信的苹果工程师当场编写)。
+ (BOOL)isCompatibleWithResponsiveScrolling {
if (NSAppKitVersionNumber <= 1561. /* NSAppKitVersionNumber10_13 */) {
return NO;
} else {
return YES;
}
}
我确信这都是公开的、记录在案的 API 的一部分,尽管文档很少(出乎意料)。在 OS X 10.9发行说明中的新增功能中有一些关于响应式滚动的讨论。使用 NSAppKitVersionNumber 检查是为了确保在 Mojave 上运行时关闭此补丁,因为一切都是分层支持的。
推荐阅读
- python - 在 GPS 不可用的情况下从 IMU 数据计算位置
- python - aiohttp.client_exceptions.ClientConnectorError:无法连接到主机stackoverflow.com:443 ssl:默认[连接调用失败('151.101.193.69',443)]
- mysql - 将 Powershell 变量传递给 MySQL
- javascript - 使用 Navigation 5 反应本机 UseEffect 获取数据
- android - 在设备管理器中找不到 Android 设备(Windows 10 - HP 笔记本电脑,Android 8 - 摩托罗拉)
- java - intellij println 声明问题(已解决)
- webhooks - 使用 URL 链接修改 Asana 任务
- postgresql - 查看哪些对象依赖于 psql 中的对象的简单方法?
- c++ - 使用 std::optional作为类 A 的类成员
- python - TypeError:字符串索引必须是整数 - Python JSON