My team and I have been working on an existing, non-document-based Cocoa application. This is our first Cocoa app, although we've done a number of iOS apps thus far.
The app really should be document-based, though, so I've begun trying to convert it. But things here & there don't seem to be working. For example, the File -> Open menu item is permanently disabled (although I finally got the File -> Save menu item to enabled; initially it wouldn't). In addition, I can click the red X to close a window, although the File -> Close menu item itself is disabled; however, when I close the window via the X button, the dealloc method in my NSDocument implementation (SPDocumentInfo) is not invoked. I created a sample, brand-new document-based app just for comparisons; when I close a window there, the SPDocument implementation's dealloc method is indeed invoked (as I'd expect.) So that concerns me.
I made a lot of changed to the project here and there; they include:
Made SPDocumentInfo extend SPDocument like so in the .h file:
@interface SPDocumentInfo : NSDocument <NSWindowDelegate>
Implemented the following in 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; }
Edited the .plist file to add "Document types". Among other things, defined "Cocoa NSDocument Class" = "SPDocumentInfo".
Altered some connections in SPDocumentInfo to match the connections in the sample document-based app. For example, in SPDocument.nib, the File's Owner (which represents SPDocumentInfo) is the Window's delegate.
So, I'm wondering if there are other sorts of things I might be missing in converting to a doc-based app. Or, are there any guides on how to do this? (I've looked but couldn't find any). Or should I just start over with a new document-based app and try to retrofit all of our stuff into it? In general, does anyone have any experience with this?
An often found suggestion is to create a new document based application and move all you existing code in there. This can be cumbersome for a large workspace with all kinds of stuff nicely configured. Let alone breaking version control.
I took the following simple steps and it worked:
- Generate a document based application
from this this generated project, copy the following section from the Info.plist (open the file with a normal text-editor):
<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>
and paste it in the Info.plist file in your own project.
Copy Document.swift from the generated document-based project into your own project.
it contains a method:
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) }
It creates a new window just the way you application normally would. If you storyboard has only one windowcontroller the 'withIDentifier'-field can contain something arbitrary. If you have more window controllers in your storyboard, the identifier needs to correspond to the right windowcontroller for new documents.
Everything @Hans' answer provides is correct, but the final change that is needed is totally, stupidly trivial, but adds a nontrivial amount of functionality that comes with document-based apps:
In the Main.storyboard
file, the document
element has an extra property that needs to be deleted: initialViewController="XXX-XX-XXX"
.
It is probably the last thing on second line. Remove this, and the Save…
menu option, along with a few other menu options, will properly be enabled by default, and the app will properly recognize the document object upon launch.
Okay, this time I legitmately do have a solution to present.
It turns out I had a "window" instance variable in SPDocumentInfo (which as you'd guess pointed to the NSWindow associated with the document). That appeared to caused a chain of events (or more likely, prevented a chain of events) which led to SPDocumentInfo's dealloc not being called when it should have. I didn't catch that when I was comparing my project to the sample doc-based project, because apparently SPDocument also has a member variable called "window" which is also connected to the relevant NSWindow. I saw that connection in the sample project, and it looked identical to my project's connection, so I didn't think twice about it.
In other words, part of my problem was that I just coincidentally decided to connect up a "window" outlet NSDocument implementation, and didn't realize that I was actually shadowing a superclass variable (which I'm guessing is, unlike mine, configured as "assign" and not "retain").
So, things seem okay at this point, and I think I can declare that it is indeed possible (and my nagging issue notwithstanding, generally painless) to convert from a non-doc-based app to a doc-based one.
This is more of an opinion than a direct answer, but if you're new to the Mac side and to document-based applications, your path of least resistance would definitely be to create a new doc-based Xcode project from the template and move your relevant code over, plugging it into the template places where needed.
精彩评论