没有Storyboard,NSApplicationDelegate无法正常工作

问题描述 投票:12回答:3

我正在尝试在macOS Sierra上的Xcode 8(稳定版)中创建一个没有故事板的macOS应用程序。但是,我的AppDelegate甚至没有被发起。这是我的代码:

import Cocoa

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
    var window: NSWindow!

    override init() {
        super.init()
        print("Init")
    }

    func applicationDidFinishLaunching(_ aNotification: Notification) {
        print("Finished launching")

        // Create a window
        window = NSWindow()

        // Add the view controller
        let viewController = ViewController()
        window.contentView?.addSubview(viewController.view)

        // Show the window
        window.makeKeyAndOrderFront(nil)
    }
}

initapplicationDidFinishLaunching(_ aNotification: Notification)都没有被召唤。任何帮助将非常感激。

swift xcode macos
3个回答
16
投票

你需要在这里做一些事情

  1. 从plist中删除NSMainStoryboardFile键/值
  2. 创建一个NSApplication子类并将其分配给Principal Class (NSPrincipalClass)密钥。

assign custom class to principal class

该名称必须完全符合您的模块名称。

  1. 在NSApplication子类中手动实例化您的委托,并将其分配给delegate属性。

确保对代理对象保持强引用。我刚刚在这里使用了let

class GrookApplication: NSApplication {

    let strongDelegate = AppDelegate()

    override init() {
        super.init()
        self.delegate = strongDelegate
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}

例如一个简单的代表。

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

    override init() {
        super.init()
        print("wassup")
        //conceptual proof of life init override
        //wait until applicationDidFinishLaunching , specially for UI
    }

    var window: NSWindow!
    func applicationDidFinishLaunching(_ aNotification: Notification) {
        print("yo! I'm alive")
        window = NSWindow(contentRect: NSRect(x: 0, y: 0, width: 200, height: 200), styleMask: .titled, backing: .buffered, defer: false)
        window.makeKeyAndOrderFront(nil)
    }

}

EDIT 2018已验证High Sierra

不要尝试在init中执行窗口或查看控制器初始化,这会导致双应用程序初始化问题和崩溃。该应用程序尚未在此阶段完成启动。等到applicationDidFinishLaunching开火任何重要的操作。


6
投票

沃伦伯顿接受的答案,利用对@NSApplicationMain注释的AppDelegate实例的强引用不再有效。我已经在OS X High Sierra上证实了这一点,Alex Sieroshtan评论说它在OS X Yosemite中也没有用。正如Tyler Durden指出的那样,失败点就是这样的信息:

Assertion failure in -[X.XApplication init], /Library/Caches/com.apple.xbs/Sources/AppKit/AppKit-1504.82.104/AppKit.subproj/NSApplication.m:1778
2017-04-08 13:25:35.761585+0100 X
[9073:1059806][General] An uncaught exception was raised 2017-04-08 13:25:35.761601+0100 X
[9073:1059806][General] Creating more than one Application

我自己一直在努力解决这个问题,但通过不少的实验,我想出了两个最新的解决方案。

Option 1: Continue to use @NSApplicationMain via a workaround

我发现你可以改变接受的答案的代码来解决bug。这样做的方法是不在名为super.init()的类中调用AppDelegate方法。

什么?

真。我认为有一个过于急切的断言计算AppDelegate(或沿着这些线的某些逻辑)所做的次数,因此对super.init()的调用以及override init()块的完成被计算。您有两种解决方法可供选择:

  1. 不要打电话给super.init():这实际上是可能的,completely healthy用于NSObject,至少在macOS中。然而,你失去了在self区块中引用override init()的能力。
  2. 根本不要覆盖init():考虑在applicationWillFinishLaunching(:)等生命周期方法中执行init过程。

当然,我不推荐其中任何一种。

Option 2: Give up on the @NSApplicationMain method altogether

@NSApplicationMain只是一个我们可以近似的宏观。幸运的是,我遇到了James H Fisher的blog post解释如何。我会在一瞬间引用重要的事情。

如果您已在任何地方写过@NSApplicationMain,请在继续执行这些说明之前将其删除。

无需更改您的Info.plist文件

NSPrincipalClass的关键:值对应保持其默认值:

<key>NSPrincipalClass</key>
<string>NSApplication</string>

使用main.swift而不是子类化NSApplication

该文件必须被称为main.swift;对于斯威夫特来说,这是一个special exception“表达式不允许在顶级”规则。

import AppKit

let app = NSApplication.shared
let delegate = AppDelegate()
app.delegate = delegate

_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)

逻辑

James H Fisher解释说,参考NSApplication documentation

[文件]

每个应用程序必须只有一个NSApplication实例(或NSApplication的子类)。你的程序的main()函数应该通过调用shared()类方法来创建这个实例。

[詹姆士]

首先,main.swift运行NSApplication.shared,并将此NSApplication对象分配给myApp。请注意,文档引用了main()函数,即使在Swift中也没有!相当于main.swift文件。

接下来,main.swift实例化你的AppDelegate类,并将其指定为.delegatemyApp。您现在可以看到默认项目选择调用类AppDelegate的原因:它在.delegate上设置为NSApplication

最后,main.swift调用函数NSApplicationMain(...) ...函数NSApplicationMain(...)是Cocoa应用程序的入口点。 NSApplicationMain(...)永远不会回来;相反,它设置UI事件循环,并最终使用C exit(...)函数退出。

此外,this StackOverflow post详细介绍了为什么使用sharedApplication补救“创建多个应用程序”错误。

......这就是你所需要的!希望这有助于帮助别人。


1
投票

如果有人正在寻找Swift版本(基于@WarrenBurtons答案)。

AppDelegate中

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
    var window: NSWindow?

    func applicationDidFinishLaunching(_ aNotification: Notification) {
        // Insert code here to initialize your application
        window = NSWindow(contentViewController: RootViewController())
        window?.makeKeyAndOrderFront(self)
    }
}

class RootViewController: NSViewController {
    override func loadView() {
      self.view = NSView()
      self.view.frame = NSRect(x: 0, y: 0, width: 600, height: 400)
  }
}

NSApplication子类

import Cocoa

class Application: NSApplication {
    let strongDelegate = AppDelegate()

    override init() {
        super.init()
        self.delegate = strongDelegate
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

Info.plist条目

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    ...
    <key>NSPrincipalClass</key>
    <string>$(PRODUCT_MODULE_NAME).Application</string>
    ...
</dict>
</plist>

我还为此创建了一个要点,我将及时了解新的Xcode / Swift版本。 https://gist.github.com/florieger/7ac5e7155f6faf18666f92f7d82f6cbc

编辑:确保删除Main.storyboard / MainMenu.xib,否则您最终可能会在UI调试器中使用两个Windows。

© www.soinside.com 2019 - 2024. All rights reserved.