【问题标题】:OS X storyboard calls viewDidLoad before applicationDidFinishLaunchingOS X 故事板在 applicationDidFinishLaunching 之前调用 viewDidLoad
【发布时间】:2016-04-17 20:04:48
【问题描述】:

我使用故事板在 Xcode 中创建了一个 Mac OS X 应用程序。出于某种原因,AppDelegate 中的 applicationDidFinishLaunching 方法在 NSViewControllers 中的 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)

    }
}

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

【问题讨论】:

  • 谢谢你,你是救命恩人!!!

标签: xcode macos cocoa nsstoryboard


【解决方案1】:

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

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

如果您还没有,我建议您查看 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 之类的内容。

【讨论】:

  • 是的,添加到答案中。
  • 我正在尝试您的方法(在 Swift 中),但我无法让窗口出现。请查看我更新的问题。
  • 我假设你这样做了,因为你没有遇到崩溃,但是在 NSWindowController 上为窗口设置了故事板 ID?
  • 是的,屏幕截图中显示的 NSUserDefaults 示例窗口的 Storyboard ID 设置为 MainWindow。
  • 生命周期不同,但这种差异的合理性是什么? Xcode 在模板中放置了一个 applicationDidFinishLaunching 存根,就好像它应该被使用一样——如果不用于初始化应用程序,这个方法的用例是什么?
【解决方案2】:

正如问题作者所说:

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

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

解决方案

您需要使您的 WindowController 实例位于applicationDidFinishLaunching(_:) 的范围之外。要完成它,您可以为您的 AppDelegate 类声明一个 NSWindowController 专有性,并将您创建的 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
    }
}

【讨论】:

    【解决方案3】:

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

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

    (我在我的应用中尝试过,效果很好)

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

    【讨论】:

      【解决方案4】:

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-07-16
        • 1970-01-01
        • 1970-01-01
        • 2023-03-21
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多