【问题标题】:WebView not showing in NSWindowWebView 未在 NSWindow 中显示
【发布时间】:2010-08-30 16:01:18
【问题描述】:

我正在开发一个 C++ 应用程序,我需要在其中显示一个带有 WebKit WebView 的 NSWindow。我编写了将管理创建和显示窗口的 Objective-C 类,但其中包含的 WebView 不显示。这是我的代码。知道出了什么问题以及如何解决吗?

我正在编译下面的代码 $g++ -x Objective-c++ -framework Cocoa -framework WebKit Foo.m main.m -o test

Foo.h

#import <Cocoa/Cocoa.h>
#import <WebKit/WebKit.h>

@interface Foo :NSObject {
 NSWindow *window;
 WebView *view;
}

- (void)displayWindow;

@end

Foo.m

#import "Foo.h"

@implementation Foo

- (id)init {

 self = [super init];

 // Window Container
 window = [[NSWindow alloc] initWithContentRect:NSMakeRect(500.0f,500.0f,250.0f,250.0f)
           styleMask:NSBorderlessWindowMask
             backing:NSBackingStoreNonretained
            defer:NO];

 // WebView
 view = [[WebView alloc] initWithFrame:NSMakeRect(0, 0, 250.0f, 250.0f)
        frameName:@"Frame"
        groupName:nil];

 [[view mainFrame] loadHTMLString:@"<html><head></head><body><h1>Hello</h1></body></html>" 
        baseURL:nil];

 return self;
}

- (void)displayWindow {
 NSLog(@"In Display window");

 [window setContentView:view];
 [window setLevel:NSStatusWindowLevel];
 [window orderFrontRegardless];

 sleep(5); // leave it up for 5 seconds

}

- (void)dealloc {
 [window release];
 [super dealloc];
}

@end

main.m

#import "Foo.h"

int main() {

 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 [NSApplication sharedApplication];

 Foo *foo = [[Foo alloc] init];
 [foo displayWindow];
 [foo release];

 [pool release];

 return 0;
}

【问题讨论】:

    标签: c++ objective-c cocoa webkit objective-c++


    【解决方案1】:

    您需要运行运行循环。如果你只是命令窗口进入然后退出,这正是将会发生的事情:窗口将出现,然后(五秒后)你的程序将退出。您可以通过将应用程序(您创建但不以其他方式使用)告诉run 来运行运行循环。

    在 Cocoa 应用程序的主线程上,sleep 总是错误的答案。它的可可表亲+[NSThread sleepUntilDate:]+[NSThread sleepForTimeInterval:] 也是如此。运行循环会让你告诉它运行一段固定的时间,但这不会让应用程序运行;您确实需要向应用程序发送run 消息,该消息不会提供在固定间隔后退出的机会。

    解决方案是首先创建一个NSTimer 对象,其目标是应用程序,其选择器是@selector(terminate:)Create it scheduled 且不重复,间隔设置为 5 秒。 (按计划创建它意味着您不需要单独计划它——从您创建它的那一刻起,它就已经准备就绪。)然后,向应用程序发送run 消息。五秒钟后,运行循环将触发计时器,这将告诉应用程序自行终止。这是假设您确实有充分的理由让您的应用程序在五秒后退出。

    正如 Yuji 所说,现代 Cocoa 中的每个窗口都应该使用NSBackingStoreBuffered

    别忘了释放你创造的东西;在视图的情况下,您目前忘记了这一点。请参阅Memory Management Programming Guide for Cocoa

    一旦你有了这个工作,我建议你转向这个应用程序的更典型的架构:

    • 创建 NSObject 的子类,并将该类的实例作为您应用程序的委托。
    • 将窗口及其 WebView 放入 nib,并让应用委托创建一个窗口控制器来加载和拥有该 nib 的内容。
    • 应用代理还应负责将页面加载到 WebView 中并设置自终止计时器。
    • 最后,创建一个 nib 来保存应用程序的主菜单(菜单栏的内容)和应用程序委托。 Interface Builder 的第一部分有一个模板;您可以通过从库中拖入一个空白对象,在 ⌘6 Inspector 上设置其类,然后将连接从应用程序拖到对象来创建应用程序委托对象。然后,您可以将 main 简化为 Xcode 的项目模板放入其中的单行:return NSApplicationMain(argc, argv);

    所有这些都将有助于您了解 Cocoa,以及您对应用程序的维护 — 将所有内容都塞进 main 将无法扩展。

    如果您还没有阅读Cocoa Fundamentals Guide,您还应该阅读。

    【讨论】:

    • 您能否将我的 cmets 添加到您的出色答案中?我挠了一个小时,为什么当我做明显的事情时他的代码不起作用......
    【解决方案2】:

    不要让它sleep。它停止处理 GUI 的主线程的执行。相反,您需要运行run loop。此外,Cocoa 需要自行设置。所以,调用[[NSApplication sharedApplication] run] 来正确设置它并运行事件循环。

    另外,不要使用缓冲模式以外的支持模式。其他模式是远古时代遗留下来的,只能使用NSBackingStoreBuffered。正如in this Apple document 所讨论的,非保留模式是支持 Classic Blue Box(OS 9 虚拟器)的残余,而 WebKit 等较新的类无法在其中运行。

    所以,实际上你需要做的是:

    1. NSBackingStoreNonretained 更改为NSBackingStoreBuffered
    2. 删除线

      sleep(5);
      
    3. 添加一行

      [[NSApplication sharedApplication] run];
      

      之后

      [foo displayWindow];
      
    4. 另外,为了让应用程序能够正确地接收来自窗口服务器的事件,您需要将其打包到一个应用程序包中。将其编译为名为@9​​87654331@ 的二进制文件,并创建以下结构:

      foo.app/
      foo.app/Contents/
      foo.app/Contents/MacOS/
      foo.app/Contents/MacOS/foo   <--- this is the executable
      

      然后您可以在 Finder 中双击 foo.app,或者直接从命令行调用 ./foo

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-09-01
      • 1970-01-01
      • 2020-03-15
      • 2023-03-16
      • 2023-04-01
      • 2011-11-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多