打开基于 NSDocument 的应用会加载两个 windows

Opening NSDocument based app loads two windows

我有一个简单的基于 NSDocument 的应用程序,它有两个文本字段(假设 git 不同)。当我打开我的文件时,它会打开两个 windows 和我的文件名。第一个 window 一切正常。第二个 UI 是空的,文件名相同。

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

override func read(from data: Data, ofType typeName: String) throws {

    if let fileString = String(data: data, encoding: String.Encoding.utf8) {

        makeWindowControllers()

        guard let vc = windowControllers.first?.contentViewController as? ViewController else {
            throw NSError(domain: NSOSStatusErrorDomain, code: unimpErr, userInfo: nil)
        }

        guard let inputString = String(data: data, encoding: .utf8) else {
            throw NSError(domain: NSOSStatusErrorDomain, code: unimpErr, userInfo: nil)
        }

        let doc = JsonConverter().convertToJSONFrom(string: inputString)

        if let a = doc["a"] as? String {
            vc.leftTextView.string = a
        }

        if let b = doc["b"] as? String {
            vc.rightTextView.string = b
        }

    } else {
        throw NSError(domain: NSOSStatusErrorDomain, code: unimpErr, userInfo: nil)
    }
}

我不明白第二个(右边)来自哪里。

编辑: 我刚刚注意到,如果我双击一个保存的文件,我会得到这个错误,奇怪的是,不会从 read() 中的任何 NSErrors 中触发功能。

嗯,从菜单打开就是我上面提到的。

编辑 2: 更正。 'Sometimes' 它打开两个 windows。但即使这样做,两者都是空的。我不明白的是这个读取函数非常简单,不会出错。感觉这一切都在幕后进行。

I don't get where the second one (on the right) is coming from.

看起来它可能来自您的 read() 函数,该函数调用 makeWindowControllers()。来自 the documentation:

This method is called by the NSDocumentController open... methods, but you might want to call it directly in some circumstances.

打开文档时,当然也会读取它,因此 makeWindowControllers() 被调用两次:一次是文档的内置打开行为,一次是您的 read() 覆盖。

Correction. 'Sometimes' it opens two windows. But even when it does that, both are empty.

也许您遇到了一个偶发错误,导致文档无法进入 read() 调用。

if I double click a saved file, I get this error

这支持了在您到达 read() 之前就出现问题的想法。您可以通过在 read() 中放置一条日志消息并在只有一个 window 打开的情况下检查该消息的控制台来很容易地找到。另外,您的应用程序委托中是否覆盖了任何 application(open...) 方法?也许其中之一是造成错误的原因。

更新:

在对一个简单的基于文档的项目进行了一番研究之后,我发现 NSDocument 确实会为您调用 makeWindowControllers(),但它会在 after[=64] 之后调用=] 它调用 read()read() 方法中的断点将显示没有视图控制器,因为 makeWindowControllers() 尚未被调用。这是我的 read() 方法:

override func read(from data: Data, ofType typeName: String) throws {
    // makeWindowControllers();
    guard let text = String(bytes: data, encoding: .utf8) else { return }
    self.content = text
}

如果我取消注释对 makeWindowControllers() 的调用,我会得到与您询问的相同的症状:两个 windows 打开,并且只有其中一个显示内容。但是,如果我创建一个新文档,我只会得到一个 window,因为 read() 方法永远不会被调用。

So this app is very basic at this point. View controller has a couple outlets and sets the text in NSTextViewDelegate's textDidChange function. But that's it. App delegate default, then the Document class. If I don't call makeWindowControllers then windowControllers is empty. If I call it, then I have 1 controller, which is what I use to update the UI. Not really sure how to get around that.

您试图在 read() 方法中做太多事情。 NSDocument 非常面向 MVC,您可以将文档视为一种控制器——它将视图和数据联系在一起。 read() 方法旨在让您读取数据,并且在此之后才会创建视图。我认为这可能是因为数据可能决定要创建哪种视图。我的文档 class 有一个 content 属性 只是一个字符串,而 read() 只是从文件中获取数据并将其粘贴到字符串中。在我的例子中,makeWindowControllers() 会自动为我调用,我的覆盖使用 content 来设置 window:

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

Also, if I comment out the call to makeWindowControllers, it never gets called when I open a file. Logging proves that its only called once with the manual call and zero times without it.

如果你的日志语句在 makeWindowControllers() 中,我对此没有很好的解释。这与我所看到的不一致,所以我会要求您 a) 检查您的工作并 b) 在此处添加更多信息,无论是在评论中还是在您的问题中。同样,我看到 makeWindowControllers() 被调用而无需显式调用它。