OS X故事板在applicationDidFinishLaunching

时间:2016-04-17 20:04:49

标签: xcode macos cocoa nsstoryboard

我使用故事板在Xcode中创建了一个Mac OS X应用程序。出于某种原因,在NSViewControllers中applicationDidFinishLaunching之后调用AppDelegate中的viewDidLoad方法。与iOS应用程序一样,我认为应该在viewDidLoad之前调用applicationDidFinishLaunching? OS X应用程序中的故事板是否在应用程序完成启动之前初始化视图控制器?

我使用applicationDidFinishLaunching方法将默认设置注册到NSUserDefaults。不幸的是,在加载故事板中的视图之后,会发生注册默认值。因此,当我使用viewDidLoad在每个视图控制器中设置视图时,尚未设置NSUserDefaults中的默认数据。如果我无法使用applicationDidFinishLaunching在OS X故事板应用程序中注册NSUserDefaults,那么在调用viewDidLoad之前如何设置默认值?

要解决此问题,在Xcode的Main.storyboard中,我关闭了主窗口的“Is Initial Controller”。我将故事板ID分配给主窗口为“MainWindow”。然后在AppDelegate中我输入了以下代码:

import Cocoa

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

    func applicationDidFinishLaunching(aNotification: NSNotification) {

        let storyboard = NSStoryboard(name: "Main", bundle: nil)
        let mainWindow = storyboard.instantiateControllerWithIdentifier("MainWindow") as! NSWindowController
        mainWindow.showWindow(nil)
        mainWindow.window?.makeKeyAndOrderFront(nil)

    }
}

应用程序不会崩溃,但现在窗口永远不会出现。下图显示了我正在使用的故事板:

enter image description here

4 个答案:

答案 0 :(得分:8)

正确,OS X中的生命周期略有不同。

您可以改为设置MainMenu xib文件并将其指定为主界面,然后在AppDelegate中的applicationDidFinishLaunching方法中,而不是让故事板成为您的初始界面(这在项目的常规设置中定义)。您可以在完成其他初始化代码后以编程方式实例化故事板。

如果你还没有,我建议你查看 OS X的可可编程:大书呆子牧场指南;他们在书中做的一件好事实际上是你摆脱了一些默认的Xcode模板的东西,而是他们让你设置你的初始视图控制器"对"明确地做的方式。

您可以在applicationDidFinishLaunching中添加类似的内容:

man javac

这假设您已经更改了"主界面"像MainMenu.xib那样。

答案 1 :(得分:1)

除了Aaron's answer之外,我发现单独包含菜单的独立Interface Builder文件(与窗口和视图控制器分开)不需要是xib; 它也可以是故事板。这使我们可以轻松修复如下:

  1. 重复您的原始故事板(Main.storyboard)并重命名至" 菜单 .storyboard"
  2. 从Menu.storyboard
  3. 删除窗口控制器和视图控制器
  4. 从Main.storyboard删除应用菜单栏。
  5. 更改您的应用目标"主界面"来自" Main.storyboard"到" Menu.storyboard"。
  6. (我在我的应用中尝试了它并且有效)

    这样可以避免从头开始重新创建应用程序的主菜单,或者弄清楚如何从#34; Main.stroyboard"移植现有的菜单。 to" Menu.xib"。

答案 2 :(得分:1)

正如问题的作者所说:

  

应用程序不会崩溃,但现在窗口永远不会出现。

尽管NicolasAaron两个都是有用的答案(我已经知道你推荐的那本书,Aaron,并将开始实现你的方式,两个故事板模式,尼古拉斯),既没有解决具体的问题窗口没有显示。

解决方案

您需要在applicationDidFinishLaunching(_:)的范围之外使您的WindowController实例生效。要完成它,您可以为NSWindowController类声明AppDelegate适当性,并在此处保留您创建的WindowController实例。

实施

在Swift 4.0中

import Cocoa

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

    var mainWindowController: NSWindowController?

    func applicationDidFinishLaunching(_ aNotification: NSNotification) {

        let storyboard = NSStoryboard(name: NSStoryboard.Name(rawValue: "Main"), bundle: nil)
        let mainWindow = storyboard.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "MainWindow")) as! NSWindowController
        mainWindow.showWindow(self)
        mainWindow.window?.makeKeyAndOrderFront(self)

        self.mainWindowController = mainWindow //After applicationDidFinishLaunching(_:) finished, the WindowController instance will persist.

}

答案 3 :(得分:0)

另一种解决方案是在视图控制器中注册NSApplicationDidFinishLaunchingNotification,并将代码从viewDidLoad移至applicationDidFinishLaunchingNotification:

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(applicationDidFinishLaunchingNotification:)
                                             name:NSApplicationDidFinishLaunchingNotification
                                           object:nil];