【问题标题】:Opening NSDocument based app loads two windows打开基于 NSDocument 的应用程序会加载两个窗口
【发布时间】:2019-04-26 15:41:03
【问题描述】:

我有一个简单的基于 NSDocument 的应用程序,它有两个文本字段(想象一下 git 不同)。当我打开我的文件时,它会打开两个带有我的文件名的窗口。在第一个窗口上一切正常。第二个的 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 中触发。

Fwiw,从菜单打开就是我上面所说的。

编辑 2: 更正。 “有时”它会打开两个窗口。但即使这样做,两者都是空的。我不明白的是,这个读取功能是如此简单,没有太多问题。感觉就像引擎盖下的一切都在侧身。

【问题讨论】:

    标签: swift macos nsdocument


    【解决方案1】:

    我不知道第二个(右侧)来自哪里。

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

    此方法由 NSDocumentController open... 方法调用,但在某些情况下您可能希望直接调用它。

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

    更正。 “有时”它会打开两个窗口。但即使这样做,两者都是空的。

    也许您偶尔会遇到一个错误,导致文档甚至无法访问 read() 调用。

    如果我双击保存的文件,我会收到此错误

    这支持了在您到达 read() 之前出现问题的想法。您可以通过在read() 中输入一条日志消息并在仅打开一个窗口的情况后检查控制台中的该消息来轻松找到该消息。另外,您的应用程序委托中是否有任何 application(open...) 方法被覆盖?也许其中之一是导致错误的原因。

    更新:

    在处理了一个简单的基于文档的项目之后,我发现NSDocument 确实为您调用了makeWindowControllers(),但它在之后调用了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() 的调用,我会得到与您询问的相同的症状:打开了两个窗口,其中只有一个显示了内容。但是,如果我创建一个新文档,我只会得到一个窗口,因为 read() 方法永远不会被调用。

    所以这个应用程序在这一点上是非常基本的。视图控制器有几个出口并在 NSTextViewDelegate 的 textDidChange 函数中设置文本。但就是这样。应用程序委托默认值,然后是 Document 类。如果我不调用 makeWindowControllers 那么 windowControllers 是空的。如果我调用它,那么我有 1 个控制器,这是我用来更新 UI 的。不太确定如何解决这个问题。

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

    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)
    }
    

    另外,如果我注释掉对 makeWindowControllers 的调用,当我打开文件时它永远不会被调用。记录证明手动调用它只调用一次,没有它调用零次。

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

    【讨论】:

    • 谢谢迦勒。所以这个应用程序在这一点上是非常基础的。视图控制器有几个出口并在 NSTextViewDelegate 的 textDidChange 函数中设置文本。但就是这样。应用程序委托默认值,然后是 Document 类。如果我不打电话给makeWindowControllers,那么windowControllers 是空的。如果我调用它,那么我有 1 个控制器,这是我用来更新 UI 的。不太确定如何解决这个问题。此外,如果我注释掉对 makeWindowControllers 的调用,当我打开文件时它永远不会被调用。日志记录证明手动调用它只调用一次,没有它调用零次。
    • 漂亮,这完全有道理。非常感谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-06-13
    • 2012-10-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-25
    • 1970-01-01
    相关资源
    最近更新 更多