如何在 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;
}