在NSOpenGLView上覆盖NSTextField

11

我已经创建了一个带有NSOpenGLView的窗口,并在其中渲染了openGL内容。

我想添加一些按钮和文本字段到视图中:我可以使用接口构建器(或代码)添加NSTextField和NSButton,但它们没有显示出来。

据文档显示,NSOpenGLView无法拥有子视图,因此我创建了自己的CustomGLView,直接从NSView派生并实现了使用NSOpenGLContext的代码。但是子视图仍然没有出现:OpenGL上下文将其覆盖。

在Windows上不存在这个问题:Windows用于托管OpenGL的窗口必须设置WS_CLIPCHILDRENWS_CHIPSIBLINGS样式,确保OpenGL表面不会遮挡任何对等体或子级(视图)。

如何在绘制OpenGL的NSView上方显示子视图?

7个回答

6
你有两个选择:
1. 创建一个仅用于文本字段的窗口。将其作为OpenGL视图主机的子窗口添加进去。最大的缺点是如果OpenGL视图被移动,你必须正确地管理其位置。
2. 设置你的视图层次结构如下:
- 层支持的视图 - 包含OpenGL层的层主机视图 - 文本字段

1
我不太清楚什么是层支持视图。http://developer.apple.com/mac/library/samplecode/LayerBackedOpenGLView/并没有暗示需要在承载视图上进行太多特殊处理。也许这是隐含在所选缓冲区中的。你真正需要做的就是:[openGLView setWantsLayer:YES] - Chris Becke
完全选择第二个选项,使用CAOpenGLLayer,忘记这个过时、有缺陷和笨拙的类NSOpenGLView。现在已经不值得使用了。 - Jay

6
只需在NSOpenGLView的子视图上调用-setWantsLayer:YES方法即可。

2
如果你这样做了,一定要确保对你的代码进行性能分析。为NSOpenGLView添加CA Layer可能会导致巨大的性能损失。额外的内存和渲染时间与你的NSOpenGLView大小成比例。对于我的项目(一个游戏),帧率减少了一半以上。 - Womble

1

我对OpenGL还不是很熟悉,但据我的理解,你可以使用具有图层支持的视图在Quartz Extreme中实现子视图的视觉效果;然而,这些可能会存在问题。由于不直接支持子视图,任何解决方案都有可能是一种黑客方式。

事实上,链接中的解决方案实际上是通过第二个窗口来实现所需Cocoa视图的覆盖OpenGL显示器,第二个窗口上显示。

以下代码(来自上述链接)是我尚未测试过的内容(再次强调,我不是OpenGL专业人员),但看起来是一种相当聪明的做法:

// This is the GL Window and view you already have
glWindow = [[GLWindow alloc] initWithContentRect:windowRect];
glView = [[[GLView alloc] initWithFrame:NSMakeRect(0, 0, windowRect.size.width, windowRect.size.height)] autorelease];
[glView translateOriginToPoint:NSMakePoint(glView.bounds.size.width/2, glView.bounds.size.height/2)];
[glWindow setContentView:glView];

// And here's your transparent UI window
uiWindow = [[TransparentWindow alloc] initWithContentRect:windowRect];
uiView = [[[NSView alloc] initWithFrame:NSMakeRect(0, 0, windowRect.size.width, windowRect.size.height)] autorelease];
[uiView translateOriginToPoint:NSMakePoint(uiView.bounds.size.width/2, uiView.bounds.size.height/2)];
uiView.wantsLayer = YES;

[uiWindow setContentView:uiView];
[glWindow addChildWindow:uiWindow ordered:NSWindowAbove];

再次声明,我没有测试过这个方法,但是看起来它可以帮助你实现你想要的视觉效果。


请查看我下面的评论,了解使用这种方式会对性能产生的影响。 - Womble

1
根据文档,NSOpenGLView 不能有子视图。即使你对 NSOpenGLView 进行子类化,也不会改变这一点。
你可以创建一个 NSView,用来容纳 NSOpenGLView 和 NSTextField。然后按正确的顺序重叠它们,使其中一个绘制在另一个之上。

我没有对NSOpenGGView进行子类化 - 我创建了一个全新的NSView子类,然后添加了代码以获取NSOpenGLContext。 - Chris Becke
1
我在Interface Builder中尝试了这个:创建了一个通用的内容NSView,并给它一个NSOpenGLView和一个NSButton。无论我以哪种方式定位它们,NSOpenGLView始终会覆盖NSButton / NSTextField。 - Chris Becke

1

1) NSOpenGLView可以拥有子视图。甚至可以有很多。

2) 一些视图、控件和其他元素被NSOpenGLView欺负的原因是由于应用程序启动时的加载过程。例如,如果你在NSOpenGLView所在的窗口的内容视图中添加滑块或文本字段,则在应用程序启动时,该文本字段很可能会出现在NSOpenGLView下面。

这是苹果的一个错误。他们知道这个问题。

即使不将子视图添加到NSOpenGLView中,您也可以很容易地解决它...

在Interface Builder中,将CustomView拖入画布(而不是视图)中。通过滑块、文本等设置它。然后在您的视图控制器中创建一个outlet(称之为topView)。然后在代码的某个地方...也许(applicationDidFinishLaunching) 添加以下行...

[_window.contentView addSubview:_topView]; (定位和布局) 这将与您在IB中手动将其拖入contentView时完全相同。只是它会以正确的Z位置绘制这个可恶的东西。

这样做会使您失去IB的约束,并且必须手动处理。

你也可以通过子类化CAOpenGLLayer并将其用作常规NSView内部的支持层。在这种情况下,它会正确地绘制...

以下是苹果希望这样做的方式。CALayers是一个救星 ;)

输入以下字符串**NSOpenGLLayer**进行搜索并按Enter键以到达所在位置... NSOpenGLLayer

希望这可以帮助....


1

该文本可以被渲染成纹理 -- 我曾在一个项目中使用过这个方法,浏览了很多示例代码,最终发现了苹果公司的GLString演示代码,这是一个非常宝贵的教程资源:

http://developer.apple.com/library/mac/#samplecode/CocoaGL/Listings/GLString_m.html

我还没有尝试添加按钮,但你当然可以自己画一个,然后比较点击事件的位置和你的按钮的位置...


1
这是我的解决方案:
1)创建一个父级NSView(我们称之为parentView)。
2)将NSOpenGLView子项添加到parentView中。
3)添加另一个NSView子项到parentView中(确保此项在层次结构中的OpenGLView之后)。您可以向此视图添加其他文本字段等。
4)在父ViewController中确保调用[parentView setWantsLayer:TRUE];我在-(void)viewWillAppear中执行了此操作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接