无法使用故事板自定义实例化的窗口控制器

10

我尝试从Storyboard自定义实例化窗口控制器时,感觉像是遇到了一个bug。我正在使用NSStoryboard.instantiateController(identifier:creator:),这是MacOS 10.15的新功能。有问题的代码块如下:

let mainWC = storyboard.instantiateController(identifier: "id") { aDecoder in  
    MainWindowController(coder: aDecoder)  
} 

我已经成功地使用了基本上完全相同的代码来自定义实例化主视图控制器,然后将该视图分配给一个新窗口和一个新的窗口控制器。那很好用。我也可以通过instantiateController(identifier:)老式方法实例化窗口控制器而不是自定义初始化。但是,当我尝试上面的代码来自定义实例化窗口控制器时,就会出现以下错误:

-[NSClassSwapper _createControllerForCreator:coder:]中的断言失败...自定义实例化的控制器必须调用-[super initWithCoder:]

请注意,我的自定义视图控制器类(可行)和我的自定义窗口控制器类MainWindowController(无效)都实现了微不足道的初始化程序:

required init?(coder: NSCoder) {  
    super.init(coder: coder)  
}

我知道这个功能是从10.15版本的操作系统开始新增的,但是文档中写到它应该适用于window controllers和view controllers,而且错误信息对我来说毫无意义。


你使用 NSStoryboard.instantiateController(identifier:creator:)NSStoryboard.instantiateController(identifier:) 还是 NSStoryboard.instantiateInitialController(creator:) - Willeke
NSStoryboard.instantiateController(identifier:creator:) - Tyler Anderson
该链接指向 NSStoryboard.instantiateInitialController(creator:) 的文档。 - Willeke
我觉得这看起来像一个 bug。 - Willeke
2个回答

6

我也遇到了同样的问题,经过一番思考,我想出了解决办法。

首先,我为什么需要这个功能?我想在从Storyboard构建视图控制器层次结构之前注入一些依赖项。我猜这就是API的设计初衷。 但是,如果该方法有效,我怎样才能将注入信息传递给视图控制器层次结构呢?

因此,由于该方法对视图控制器没有错误,我决定直接在根视图控制器中注入信息。

因此,在我的Storyboard中:

  • 一个名为“my-window-controller”的窗口控制器场景,其中窗口只指向一个空视图控制器。
  • 一个名为“root-view-controller”的视图控制器场景,其中描述了整个视图层次结构。

在我想创建那个视图控制器的任何地方,我只需要执行以下操作:

func instanciateWindowController(storyboard: NSStoryboard) -> NSWindowController {

    //  Load the (empty) window controller scene
    let wcSceneIdentifier   = NSStoryboard.SceneIdentifier("my-window-controller")
    let windowController    = storyboard.instantiateController(withIdentifier: wcSceneIdentifier)
            as! NSWindowController

    //  Load the root view controller using the creator trick to inject dependencies
    let vcSceneIdentifier   = NSStoryboard.SceneIdentifier("root-view-controller")
    let viewController      = storyboard.instantiateController(identifier: vcSceneIdentifier,
                                                               creator: { coder in
        return MyOwnViewController.init(coder: coder,
                                        text:   "Victoire !") // just pass here your injection info
    })

    //  Associate the window controller and the root view controller
    windowController.contentViewController  = viewController

    return windowController
}

使用

class MyOwnViewController: MSViewController {
    init?(coder:   NSCoder,
          text:    String) { // receive here the injection information
        print(text) // use the injection information here
        super.init(coder: coder)
    }

    // Not used, but required
    required init?(coder:   NSCoder) {
        super.init(coder: coder)
    }
}

1
当时我真希望一个月前就学会这个。最后我只得手动在代码中重新创建了我的主窗口、窗口控制器和工具栏,并将它们都绑定到根视图控制器上。但是你的答案允许使用storyboard。看起来关键在于将窗口链接到一个虚拟的视图控制器;我曾尝试过将我的窗口控制器与故事板中的主视图控制器链接,结果引起了各种麻烦。我认为这是一个可接受的答案。 - Tyler Anderson
如果您对依赖注入和故事板感兴趣,可以看一下这个链接:https://dev59.com/ooPba4cB1Zd3GeqPqUww 。在我的回答中,我提出了我认为是使用故事板进行依赖注入的“正确方式”。大多数人建议使用prepareForSegue,但我认为这不是一个好的解决方案。 - AirXygène

0

这是反馈编号#FB7626059,如果你也遇到了这个问题,可以一起加入。


1
谢谢,我也在FB7595235下提交了这个问题。您如何查看其他人提交的反馈? - Tyler Anderson
很遗憾,你不能这样做。但是据说如果在报告中包含其他 FB #s,可以有所帮助。 - Wil Shipley
你可以将你的错误报告同时发布到 https://openradar.appspot.com。虽然苹果不使用该网站,但你仍然可以手动跨平台发布他们的回复。这可能有些糟糕,但它可以使你的工单更易于被发现、搜索等。 - Alexander

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接