如何在 Mac 上为我的应用分配一种文件类型
How do assign a type of file to my app on Mac
我尝试为我的应用程序指定一个文件类型。
在Info.plist中我添加:
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>type</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>icon</string>
<key>CFBundleTypeName</key>
<string>My Project</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSTypeIsPackage</key>
<false/>
</dict>
</array>
在Main.mm中:
....
-(BOOL) application:(NSApplication *)sender openFile:(NSString *)filename {
NSLog(@"Opened by file");
return YES;
}
@end
int main(int argc, char* argv[]) {
[NSApplication sharedApplication];
[[[[Window alloc] init] autorelease] makeMainWindow];
[NSApp run];
return 0;
}
但是当我尝试双击我的文件类型时,该应用程序只打开警告:无法打开,MyApp 无法打开格式的文件。此外,根本不会调用来自 NSLog 的消息。
您发布的代码存在几个问题,但我通过一些修改获得了所需的行为。
我假设这是您的 window 接口和实现:
@interface Window : NSWindow <NSApplicationDelegate>
- (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename;
@end
@implementation Window
- (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename {
NSLog(@"Opened by file");
return YES;
}
@end
使用 window 对象作为应用程序委托非常奇怪。通常,您有一个控制器对象,它拥有和管理 window 并且还充当应用程序的委托。
无论如何,通过将 main()
函数更改为以下内容,仍然可以获得...嗯,功能行为:
int main(int argc, const char * argv[]) {
[NSApplication sharedApplication];
Window *window = [[[Window alloc] init] autorelease];
NSApp.delegate = window;
[window makeKeyAndOrderFront:nil];
[NSApp run];
return 0;
}
有两个显着变化。首先,您的问题是您没有将 window 实例设置为应用程序委托。其次,IIRC,你不应该直接调用 -makeMainWindow
;相反,该方法的存在使您可以根据需要覆盖它。如果你想在屏幕上显示 window,你调用 -makeKeyAndOrderFront:
.
打开文件应该会在控制台中显示记录的行(如果您使用的是 Xcode 12.5.1,请根据需要调整日志大小 window 以解决显示错误)。
在手动引用计数下,我相信这会泄漏内存,因为没有创建自动释放池,但我没有在控制台中看到任何常见的警告。不管怎样,虽然这段代码有效,但它会导致一个相当不受欢迎的场景。应用程序中没有主菜单,因此要退出它,您必须使用 Dock。创建的 window 也很小,没有调整大小等功能
编辑:示例项目位于 https://github.com/NSGod/OpenFile。
NSWindow 的以下子class 应允许您保存具有唯一“.jaf”扩展名的文件,然后双击该文件将其重新打开到应用程序中。 info.plist 并不像我最初想象的那么重要;我没有更改 Xcode 创建的那个。对于这个基于非文档的应用程序来说,最重要的似乎是调用 NSApplicationDelegate 方法 -(BOOL) application: openFile。 NSApplicationDelegate 被添加到 NSWindow subclass 而不是通常情况下有一个单独的 AppDelegate。正常工作时,当双击 .jaf 文件后调用此方法时,您应该会听到哔声;我找不到 NSLog 输出。为了运行Xcode中的demo,首先创建一个objc项目,删除‘main.m’文件中的所有内容和copy/paste下面的源代码。删除预先提供的 AppDelegate class 以避免重复符号。在权利中,将 App Sandbox 设置为 NO,并将只读设置为零。制作 JAF 应用程序后,使用它以“.jaf”扩展名将文件保存到桌面。然后复制应用程序(显示在 Finder 中)并将其 copy/paste 放入应用程序文件夹中。下一步很关键;右键单击您刚刚创建的文件,然后使用“获取信息”或“打开方式”将文件设置为始终使用您新建的应用程序打开。此时,您应该可以双击 xxxx.jaf 文件并在您的应用程序中打开它并发出哔哔声。
#import <Cocoa/Cocoa.h>
@interface Window : NSWindow <NSApplicationDelegate> {
NSTextView *txtView;
}
- (instancetype)initWithContentRect:(NSRect)contentRect styleMask:(NSWindowStyleMask)style backing:(NSBackingStoreType)backingStoreType defer:(BOOL)flag;
-(void) buildMenu;
-(void) openAction;
-(void) saveAction;
@end
@implementation Window
#define _wndW 700
#define _wndH 550
- (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename {
NSLog(@"This comes from JAF : filename = %@.",filename);
NSBeep(); // Listen for this.
NSError *error;
NSURL *url = [NSURL fileURLWithPath:filename];
NSString *fileStr = [[NSString alloc] initWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error];
if (!fileStr) {
NSLog(@"Unable to open file %@", error);
} else {
[txtView setString:fileStr];
}
return YES;
}
-(void) buildMenu {
// **** Menu Bar **** //
NSMenu *menubar = [NSMenu new];
[NSApp setMainMenu:menubar];
// **** App Menu **** //
NSMenuItem *appMenuItem = [NSMenuItem new];
NSMenu *appMenu = [NSMenu new];
[appMenu addItemWithTitle: @"Quit" action:@selector(terminate:) keyEquivalent:@"q"];
[appMenuItem setSubmenu:appMenu];
[menubar addItem:appMenuItem];
}
-(void) openAction {
NSOpenPanel *op = [NSOpenPanel openPanel];
[op setAllowedFileTypes:[NSArray arrayWithObjects: @"jaf", @"txt", nil]];
[op beginSheetModalForWindow: self completionHandler: ^(NSInteger returnCode) {
if (returnCode == NSModalResponseOK) {
NSURL *url = [op URL];
NSError *error;
NSString *fileStr = [[NSString alloc] initWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error];
if (!fileStr) {
NSLog(@"Unable to open file %@", error);
} else {
[self->txtView setString:fileStr];
}
}
}];
}
-(void) saveAction {
NSSavePanel *sp = [NSSavePanel savePanel];
[sp setTitle:@"Save contents to file"];
[sp setAllowedFileTypes:[NSArray arrayWithObjects: @"jaf", nil]];
[sp setNameFieldStringValue: @".jaf"];
[sp beginSheetModalForWindow: self completionHandler: ^(NSInteger returnCode) {
if (returnCode == NSModalResponseOK) {
NSURL *url = [sp URL];
NSString *viewStr = [[self->txtView textStorage] string];
NSError *err;
BOOL fileSaved = [viewStr writeToURL:url atomically:YES encoding:NSUTF8StringEncoding error:&err];
if (!fileSaved) { NSLog(@"Unable to save file due to error: %@", err);}
}
}];
}
- (instancetype)initWithContentRect:(NSRect)contentRect styleMask:(NSWindowStyleMask)style backing:(NSBackingStoreType)backingStoreType defer:(BOOL)flag {
self = [super initWithContentRect:NSMakeRect(0, 0, _wndW, _wndH) styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable backing:NSBackingStoreBuffered defer:NO];
[self setTitle: @"Test window"];
[self center];
[self makeKeyAndOrderFront: nil];
// ****** NSTextView with Scroll ****** //
NSScrollView *scrlView = [[NSScrollView alloc] initWithFrame:NSMakeRect( 10, 10, _wndW - 20, _wndH - 80 )];
[[self contentView] addSubview:scrlView];
[scrlView setHasVerticalScroller: YES];
[scrlView setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable ];
txtView = [[NSTextView alloc] initWithFrame:NSMakeRect( 0, 0, _wndW - 20, _wndH - 80 )];
[scrlView setDocumentView: txtView];
// **** Open Button **** //
NSButton *openBtn =[[NSButton alloc]initWithFrame:NSMakeRect( 30, _wndH - 50, 95, 30 )];
[openBtn setBezelStyle:NSBezelStyleRounded ];
[openBtn setTitle: @"Open"];
[openBtn setAutoresizingMask: NSViewMinYMargin];
[openBtn setAction: @selector (openAction)];
[[self contentView] addSubview: openBtn];
// **** Save Button **** //
NSButton *saveBtn =[[NSButton alloc]initWithFrame:NSMakeRect( 130, _wndH - 50, 95, 30 )];
[saveBtn setBezelStyle:NSBezelStyleRounded ];
[saveBtn setTitle: @"Save"];
[saveBtn setAutoresizingMask: NSViewMinYMargin];
[saveBtn setAction: @selector (saveAction)];
[[self contentView] addSubview: saveBtn];
return self;
}
- (BOOL)windowShouldClose:(id)sender {
[NSApp terminate:sender];
return YES;
}
@end
int main() {
NSApplication *application = [NSApplication sharedApplication];
Window *window = [[Window alloc]init];
[window buildMenu];
[application setDelegate:window];
[application activateIgnoringOtherApps:YES];
[NSApp run];
return 0;
}
我尝试为我的应用程序指定一个文件类型。
在Info.plist中我添加:
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>type</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>icon</string>
<key>CFBundleTypeName</key>
<string>My Project</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSTypeIsPackage</key>
<false/>
</dict>
</array>
在Main.mm中:
....
-(BOOL) application:(NSApplication *)sender openFile:(NSString *)filename {
NSLog(@"Opened by file");
return YES;
}
@end
int main(int argc, char* argv[]) {
[NSApplication sharedApplication];
[[[[Window alloc] init] autorelease] makeMainWindow];
[NSApp run];
return 0;
}
但是当我尝试双击我的文件类型时,该应用程序只打开警告:无法打开,MyApp 无法打开格式的文件。此外,根本不会调用来自 NSLog 的消息。
您发布的代码存在几个问题,但我通过一些修改获得了所需的行为。
我假设这是您的 window 接口和实现:
@interface Window : NSWindow <NSApplicationDelegate>
- (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename;
@end
@implementation Window
- (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename {
NSLog(@"Opened by file");
return YES;
}
@end
使用 window 对象作为应用程序委托非常奇怪。通常,您有一个控制器对象,它拥有和管理 window 并且还充当应用程序的委托。
无论如何,通过将 main()
函数更改为以下内容,仍然可以获得...嗯,功能行为:
int main(int argc, const char * argv[]) {
[NSApplication sharedApplication];
Window *window = [[[Window alloc] init] autorelease];
NSApp.delegate = window;
[window makeKeyAndOrderFront:nil];
[NSApp run];
return 0;
}
有两个显着变化。首先,您的问题是您没有将 window 实例设置为应用程序委托。其次,IIRC,你不应该直接调用 -makeMainWindow
;相反,该方法的存在使您可以根据需要覆盖它。如果你想在屏幕上显示 window,你调用 -makeKeyAndOrderFront:
.
打开文件应该会在控制台中显示记录的行(如果您使用的是 Xcode 12.5.1,请根据需要调整日志大小 window 以解决显示错误)。
在手动引用计数下,我相信这会泄漏内存,因为没有创建自动释放池,但我没有在控制台中看到任何常见的警告。不管怎样,虽然这段代码有效,但它会导致一个相当不受欢迎的场景。应用程序中没有主菜单,因此要退出它,您必须使用 Dock。创建的 window 也很小,没有调整大小等功能
编辑:示例项目位于 https://github.com/NSGod/OpenFile。
NSWindow 的以下子class 应允许您保存具有唯一“.jaf”扩展名的文件,然后双击该文件将其重新打开到应用程序中。 info.plist 并不像我最初想象的那么重要;我没有更改 Xcode 创建的那个。对于这个基于非文档的应用程序来说,最重要的似乎是调用 NSApplicationDelegate 方法 -(BOOL) application: openFile。 NSApplicationDelegate 被添加到 NSWindow subclass 而不是通常情况下有一个单独的 AppDelegate。正常工作时,当双击 .jaf 文件后调用此方法时,您应该会听到哔声;我找不到 NSLog 输出。为了运行Xcode中的demo,首先创建一个objc项目,删除‘main.m’文件中的所有内容和copy/paste下面的源代码。删除预先提供的 AppDelegate class 以避免重复符号。在权利中,将 App Sandbox 设置为 NO,并将只读设置为零。制作 JAF 应用程序后,使用它以“.jaf”扩展名将文件保存到桌面。然后复制应用程序(显示在 Finder 中)并将其 copy/paste 放入应用程序文件夹中。下一步很关键;右键单击您刚刚创建的文件,然后使用“获取信息”或“打开方式”将文件设置为始终使用您新建的应用程序打开。此时,您应该可以双击 xxxx.jaf 文件并在您的应用程序中打开它并发出哔哔声。
#import <Cocoa/Cocoa.h>
@interface Window : NSWindow <NSApplicationDelegate> {
NSTextView *txtView;
}
- (instancetype)initWithContentRect:(NSRect)contentRect styleMask:(NSWindowStyleMask)style backing:(NSBackingStoreType)backingStoreType defer:(BOOL)flag;
-(void) buildMenu;
-(void) openAction;
-(void) saveAction;
@end
@implementation Window
#define _wndW 700
#define _wndH 550
- (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename {
NSLog(@"This comes from JAF : filename = %@.",filename);
NSBeep(); // Listen for this.
NSError *error;
NSURL *url = [NSURL fileURLWithPath:filename];
NSString *fileStr = [[NSString alloc] initWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error];
if (!fileStr) {
NSLog(@"Unable to open file %@", error);
} else {
[txtView setString:fileStr];
}
return YES;
}
-(void) buildMenu {
// **** Menu Bar **** //
NSMenu *menubar = [NSMenu new];
[NSApp setMainMenu:menubar];
// **** App Menu **** //
NSMenuItem *appMenuItem = [NSMenuItem new];
NSMenu *appMenu = [NSMenu new];
[appMenu addItemWithTitle: @"Quit" action:@selector(terminate:) keyEquivalent:@"q"];
[appMenuItem setSubmenu:appMenu];
[menubar addItem:appMenuItem];
}
-(void) openAction {
NSOpenPanel *op = [NSOpenPanel openPanel];
[op setAllowedFileTypes:[NSArray arrayWithObjects: @"jaf", @"txt", nil]];
[op beginSheetModalForWindow: self completionHandler: ^(NSInteger returnCode) {
if (returnCode == NSModalResponseOK) {
NSURL *url = [op URL];
NSError *error;
NSString *fileStr = [[NSString alloc] initWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error];
if (!fileStr) {
NSLog(@"Unable to open file %@", error);
} else {
[self->txtView setString:fileStr];
}
}
}];
}
-(void) saveAction {
NSSavePanel *sp = [NSSavePanel savePanel];
[sp setTitle:@"Save contents to file"];
[sp setAllowedFileTypes:[NSArray arrayWithObjects: @"jaf", nil]];
[sp setNameFieldStringValue: @".jaf"];
[sp beginSheetModalForWindow: self completionHandler: ^(NSInteger returnCode) {
if (returnCode == NSModalResponseOK) {
NSURL *url = [sp URL];
NSString *viewStr = [[self->txtView textStorage] string];
NSError *err;
BOOL fileSaved = [viewStr writeToURL:url atomically:YES encoding:NSUTF8StringEncoding error:&err];
if (!fileSaved) { NSLog(@"Unable to save file due to error: %@", err);}
}
}];
}
- (instancetype)initWithContentRect:(NSRect)contentRect styleMask:(NSWindowStyleMask)style backing:(NSBackingStoreType)backingStoreType defer:(BOOL)flag {
self = [super initWithContentRect:NSMakeRect(0, 0, _wndW, _wndH) styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable backing:NSBackingStoreBuffered defer:NO];
[self setTitle: @"Test window"];
[self center];
[self makeKeyAndOrderFront: nil];
// ****** NSTextView with Scroll ****** //
NSScrollView *scrlView = [[NSScrollView alloc] initWithFrame:NSMakeRect( 10, 10, _wndW - 20, _wndH - 80 )];
[[self contentView] addSubview:scrlView];
[scrlView setHasVerticalScroller: YES];
[scrlView setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable ];
txtView = [[NSTextView alloc] initWithFrame:NSMakeRect( 0, 0, _wndW - 20, _wndH - 80 )];
[scrlView setDocumentView: txtView];
// **** Open Button **** //
NSButton *openBtn =[[NSButton alloc]initWithFrame:NSMakeRect( 30, _wndH - 50, 95, 30 )];
[openBtn setBezelStyle:NSBezelStyleRounded ];
[openBtn setTitle: @"Open"];
[openBtn setAutoresizingMask: NSViewMinYMargin];
[openBtn setAction: @selector (openAction)];
[[self contentView] addSubview: openBtn];
// **** Save Button **** //
NSButton *saveBtn =[[NSButton alloc]initWithFrame:NSMakeRect( 130, _wndH - 50, 95, 30 )];
[saveBtn setBezelStyle:NSBezelStyleRounded ];
[saveBtn setTitle: @"Save"];
[saveBtn setAutoresizingMask: NSViewMinYMargin];
[saveBtn setAction: @selector (saveAction)];
[[self contentView] addSubview: saveBtn];
return self;
}
- (BOOL)windowShouldClose:(id)sender {
[NSApp terminate:sender];
return YES;
}
@end
int main() {
NSApplication *application = [NSApplication sharedApplication];
Window *window = [[Window alloc]init];
[window buildMenu];
[application setDelegate:window];
[application activateIgnoringOtherApps:YES];
[NSApp run];
return 0;
}