将基本Cocoa应用程序转换为基于文档的应用程序

时间:2011-02-23 22:03:03

标签: cocoa

我和我的团队一直在研究现有的,基于非文档的Cocoa应用程序。这是我们的第一个Cocoa应用程序,尽管到目前为止我们已经完成了许多iOS应用程序。

应用程序确实应该基于文档,所以我开始尝试转换它。但是这里的事情和似乎没有工作。例如,文件 - >打开菜单项被永久禁用(虽然我最终得到了文件 - >保存菜单项以启用;最初它不会)。另外,我可以点击红色X关闭一个窗口,虽然文件 - >关闭菜单项本身已禁用;但是,当我通过X按钮关闭窗口时,我的NSDocument实现(SPDocumentInfo)中的dealloc方法不会被调用。我创建了一个样本,全新的基于文档的应用程序,仅用于比较;当我在那里关闭一个窗口时,确实调用了SPDocument实现的dealloc方法(正如我所期望的那样)。所以这让我很担心。

我在这里和那里做了很多改变的项目;它们包括:

  • Made SPDocumentInfo在.h文件中扩展SPDocument:

    @interface SPDocumentInfo : NSDocument <NSWindowDelegate>
    
  • 在SPDocumentInfo中实现了以下内容:

    - (NSString *)windowNibName {
        return @"SPDocument";
    }
    
    - (void)windowControllerDidLoadNib:(NSWindowController *) aController {
        [super windowControllerDidLoadNib:aController];
    }
    
    - (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError {
        NSString *xml = [self toXml];
        return [xml dataUsingEncoding:NSUTF8StringEncoding];
    }
    
    - (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError {
        // will make this work later
        if ( outError != NULL ) {
            *outError = [NSError errorWithDomain:NSOSStatusErrorDomain code:unimpErr userInfo:NULL];
        }
        return YES;
    }
    
  • 编辑.plist文件以添加“文档类型”。除其他外,定义“Cocoa NSDocument Class”=“SPDocumentInfo”。

  • 更改SPDocumentInfo中的某些连接以匹配基于示例文档的应用程序中的连接。例如,在SPDocument.nib中,文件的所有者(表示SPDocumentInfo)是Window的委托。

所以,我想知道在转换为基于文档的应用程序时是否还有其他类似的问题。或者,有没有关于如何做到这一点的指南? (我看了但找不到任何东西)。或者我应该重新开始使用基于文档的新应用程序并尝试将所有内容改进其中?一般来说,有没有人有这方面的经验?

4 个答案:

答案 0 :(得分:2)

这更多的是一种观点,而不是一个直接的答案,但如果你是Mac新手和基于文档的应用程序的新手,那么你的阻力最小的路径肯定是创建一个新的基于doc的Xcode项目。模板并移动相关代码,将其插入到需要的模板位置。

答案 1 :(得分:2)

好的,这次我合法有一个解决方案。

事实证明我在SPDocumentInfo中有一个“窗口”实例变量(正如你猜测的那样指向与文档相关的NSWindow)。这似乎导致了一系列事件(或者更有可能阻止了一系列事件),导致SPDocumentInfo的dealloc在它应该被调用时没有被调用。当我将我的项目与基于文档的示例项目进行比较时,我没有意识到这一点,因为显然SPDocument 有一个名为“window”的成员变量连接到相关的NSWindow。我在示例项目中看到了这个连接,它看起来与我项目的连接相同,所以我没有三思而后行。

换句话说,我的问题的一部分是我巧合地决定连接一个“窗口”插座NSDocument实现,并没有意识到我实际上是在遮蔽一个超类变量(我猜的是,不像我的,配置为“分配”而不是“保留”。)

所以,在这一点上似乎没什么问题,而且我认为我可以宣称从非基于文档的应用程序转换为基于文档的应用程序确实可能(以及我的唠叨问题,尽管通常是无痛的)。

答案 2 :(得分:1)

经常发现的建议是创建一个基于新文档的应用程序,并将所有现有代码移到那里。对于具有良好配置的各种东西的大型工作空间来说,这可能很麻烦。更别说破坏版本控制了。

我采取了以下简单的步骤,它起作用了:

  • 生成基于文档的应用程序
  • 从这个生成的项目
  • ,从Info.plist中复制以下部分(使用普通的文本编辑器打开文件):

    <key>CFBundleDocumentTypes</key>
    <array>
        <dict>
            <key>CFBundleTypeExtensions</key>
            <array>
                <string>mydoc</string>
            </array>
            <key>CFBundleTypeIconFile</key>
            <string></string>
            <key>CFBundleTypeName</key>
            <string>DocumentType</string>
            <key>CFBundleTypeOSTypes</key>
            <array>
                <string>????</string>
            </array>
            <key>CFBundleTypeRole</key>
            <string>Editor</string>
            <key>NSDocumentClass</key>
            <string>$(PRODUCT_MODULE_NAME).Document</string>
        </dict>
    </array>
    

    并将其粘贴到您自己项目的Info.plist文件中。

  • 将Document.swift从生成的基于文档的项目复制到您自己的项目中。

  • 它包含一个方法:

    override func makeWindowControllers() {
        // Returns the Storyboard that contains your Document window.
        let storyboard = NSStoryboard(name: "Main", bundle: nil)
        let windowController = storyboard.instantiateController(withIdentifier: "Document Window Controller") as! NSWindowController
        self.addWindowController(windowController)
    }
    

    它以您正常应用的方式创建一个新窗口。如果你的故事板只有一个windowcontroller,'withIDentifier'字段可以包含任意的东西。如果故事板中有更多窗口控制器,则标识符需要与新文档的右窗口控制器相对应。

答案 3 :(得分:0)

@Hans的回答所提供的一切都是正确的,但最终所需的更改完全是愚蠢的,但却增加了基于文档的应用程序附带的大量功能:

Main.storyboard文件中,document元素具有一个额外的属性,需要删除:initialViewController="XXX-XX-XXX"

这可能是第二行的最后一件事。删除此选项,默认情况下将正确启用Save…菜单选项以及其他一些菜单选项,并且该应用在启动时将正确识别文档对象。