1

我正在开发一个 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

主文件

#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;
}
4

2 回答 2

4

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

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

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

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

并且不要忘记发布您创建的内容;在视图的情况下,您目前忘记了这一点。请参阅Cocoa 的内存管理编程指南

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

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

这样做将有助于您理解 Cocoa,以及您对应用程序的维护——将所有内容都塞进其中main不会扩展。

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

于 2010-08-30T18:28:43.113 回答
1

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

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

因此,您实际上需要做的是:

  1. 更改 NSBackingStoreNonretainedNSBackingStoreBuffered.
  2. 删除线

    sleep(5);
    
  3. 添加一行

    [[NSApplication sharedApplication] run];
    

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

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

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

于 2010-08-30T19:00:27.990 回答