10

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

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

为了解决这个问题,Main.storyboard在 Xcode 中,我关闭了主窗口的“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)

    }
}

该应用程序不会崩溃,但现在该窗口永远不会出现。下图显示了我正在使用的情节提要:

在此处输入图像描述

4

4 回答 4

8

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

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

如果您还没有,我建议您查看Cocoa Programming for OS X: The Big Nerd Ranch Guide ;他们在书中所做的一件好事实际上是让您摆脱了一些默认的 Xcode 模板内容,而是让您通过明确地以“正确”的方式设置初始视图控制器。

你可以在你的 applicationDidFinishLaunching 中加入这样的东西:

NSStoryboard *mainStoryboard = [NSStoryboard storyboardWithName:@"Main" bundle:nil];
MyWindowController *initialController = (MyWindowController *)[mainStoryboard instantiateControllerWithIdentifier:@"myWindowController"];
[initialController showWindow:self];
[initialController.window makeKeyAndOrderFront:self];

这假设您已经将“主界面”更改为 MainMenu.xib 之类的东西。

于 2016-04-17T21:37:55.933 回答
4

正如问题的作者所说:

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

尽管NicolasAaron都提供了有用的答案(我已经阅读了您推荐的书,Aaron,并将开始按照您的方式实现双故事板模式,Nicolas),但都没有解决窗口不显示的具体问题。

解决方案

你需要让你的 WindowController 实例活在applicationDidFinishLaunching(_:)'s 范围之外。要完成它,您可以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)

        // After applicationDidFinishLaunching(_:) finished, the WindowController instance will persist.
        self.mainWindowController = mainWindow
    }
}
于 2017-12-12T17:00:33.847 回答
1

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

  1. 复制您的原始故事板(Main.storyboard)并将其重命名为“ Menu .storyboard”
  2. 从 Menu.storyboard 中删除窗口控制器和视图控制器
  3. 从 Main.storyboard 中删除应用程序菜单栏。
  4. 将应用目标的“主界面”从“Main.storyboard”更改为“Menu.storyboard”。

(我在我的应用程序中尝试过它并且它有效)

这避免了必须从头开始重新创建应用程序的主菜单,或者弄清楚如何将现有的主菜单从“Main.stroyboard”移植到“Menu.xib”。

于 2017-10-14T05:17:20.077 回答
0

另一种解决方案是NSApplicationDidFinishLaunchingNotification在您的视图控制器中注册并将您的代码viewDidLoadapplicationDidFinishLaunchingNotification:

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(applicationDidFinishLaunchingNotification:)
                                             name:NSApplicationDidFinishLaunchingNotification
                                           object:nil];
于 2018-06-28T11:29:26.070 回答