3

I'm writing a NSDocument-based application in Swift. I have set it to open a default document if the application is not started with a document parameter.

I've noticed that if I start the app and then immediately open another document, say using Open or Recent, the Untitled default document remains, even if it is untouched.

I was under the impression that in this workflow, the Untitled document was considered "accidental" and should be removed. Is this correct? If so, should I remove this window manually, or is there a setting I have overlooked?

pneumatics
  • 2,836
  • 1
  • 27
  • 27
Maury Markowitz
  • 9,082
  • 11
  • 46
  • 98

1 Answers1

4

The behavior you describe is not automatic. For an example of how to handle this situation, see the open-source TextEdit example code. Specifically, take a look at the -[DocumentController replaceTransientDocument:] implementation.

- (void)replaceTransientDocument:(NSArray *)documents {
    // Transient document must be replaced on the main thread, since it may undergo automatic display on the main thread.
    if ([NSThread isMainThread]) {
        NSDocument *transientDoc = [documents objectAtIndex:0], *doc = [documents objectAtIndex:1];
        NSArray *controllersToTransfer = [[transientDoc windowControllers] copy];
        NSEnumerator *controllerEnum = [controllersToTransfer objectEnumerator];
        NSWindowController *controller;

        [controllersToTransfer makeObjectsPerformSelector:@selector(retain)];

        while (controller = [controllerEnum nextObject]) {
            [doc addWindowController:controller];
            [transientDoc removeWindowController:controller];
        }
        [transientDoc close];

        [controllersToTransfer makeObjectsPerformSelector:@selector(release)];
        [controllersToTransfer release];

    // We replaced the value of the transient document with opened document, need to notify accessibility clients.
    for (NSLayoutManager *layoutManager in [[(Document *)doc textStorage] layoutManagers]) {
        for (NSTextContainer *textContainer in [layoutManager textContainers]) {
        NSTextView *textView = [textContainer textView];
        if (textView) NSAccessibilityPostNotification(textView, NSAccessibilityValueChangedNotification);
        }
    }

    } else {
        [self performSelectorOnMainThread:_cmd withObject:documents waitUntilDone:YES];
    }
}

If you look also at -openDocumentWithContentsOfURL:display:error: and a few other places that call the above method, you'll get a good idea of how they go about replacing a "transient document" (the "Untitled" document) with the opened document.

I hope this helps.

Joshua Nozzi
  • 60,946
  • 14
  • 140
  • 135
  • It does indeed, I have never come across this before. While I'm here, I've noticed newer apps skip this entirely, and generally open to a combination open/new dialog instead. If this is the preferred behavior for new apps, is there a way to hook this instead? It's not in the NSDocument dox that I read. – Maury Markowitz Dec 29 '15 at 00:25
  • The NSApplicationDelegate protocol provides a hook for "should open untitled document". This might be a good place to say "no", and present the open dialog as well. I'd give more specific code but I'm not in a place to at present. – Joshua Nozzi Dec 29 '15 at 01:14