没有Storyboard,NSApplicationDelegate无法正常工作

Zoy*_*oyt 13 macos xcode swift

我正在尝试在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)
    }
}
Run Code Online (Sandbox Code Playgroud)

无论是initapplicationDidFinishLaunching(_ aNotification: Notification)将被调用.任何帮助将非常感激.

War*_*ton 17

你需要在这里做一些事情

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

将自定义类分配给主体类

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

  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")
    }

}
Run Code Online (Sandbox Code Playgroud)

例如一个简单的代表.

@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)
    }

}
Run Code Online (Sandbox Code Playgroud)

EDIT 2018已验证High Sierra

千万不要尝试做窗口或视图控制器初始化内init这将导致双重应用程序初始化问题和崩溃.该应用程序尚未在此阶段完成启动.等到applicationDidFinishLaunching任何重大操作开始.


Jam*_*rch 10

沃伦·伯顿(Warren Burton)接受的答案,即使用对带@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
Run Code Online (Sandbox Code Playgroud)

我为此苦苦挣扎了好一阵子,但经过不小的实验,就提出了两个最新的解决方案。

选项1:@NSApplicationMain通过替代方法继续使用

我发现您可以更改已接受答案的代码以解决该错误。为此,请不要super.init()在名为的类中调用方法AppDelegate

什么?

真。我认为现在有一个过于急切的断言来计算由AppDelegate(或沿此行的一些逻辑)完成的初始化次数,因此对调用的super.init()计数以及该override init()块的完成也是如此。这里有两种解决方法:

  1. 不要叫super.init():这实际上是可能的,完全健康NSObject,至少在MacOS上。但是,您将失去selfoverride init()块中引用的能力。

  2. 不要覆盖init()在所有:考虑像一个生命周期方法的过程中做你的init进程applicationWillFinishLaunching(:)

我当然不推荐任何一个。

选项2:@NSApplicationMain完全放弃该方法

@NSApplicationMain只是一个我们可以近似自己的宏。幸运的是,我遇到了James H Fisher的博客文章,解释了如何做。一会儿,我会引用重要的内容。

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

无需更改Info.plist文件

的key:value对应NSPrincipalClass保留其默认值:

<key>NSPrincipalClass</key>
<string>NSApplication</string>
Run Code Online (Sandbox Code Playgroud)

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

该文件必须被调用main.swift; 这是Swift的“顶层不允许表达”规则的特殊例外

import AppKit

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

_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
Run Code Online (Sandbox Code Playgroud)

逻辑

James H Fisher解释并参考NSApplication 文档

[文档]

每个应用都必须恰好具有的一个实例NSApplication(或的子类NSApplication)。程序的main()函数应通过调用shared()class方法来创建此实例。

[詹姆士]

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

接下来,main.swift实例化你的AppDelegate类,并将其分配作为.delegatemyApp。现在,您可以看到为什么默认项目选择调用该类AppDelegate:将其设置为.delegateon NSApplication

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

此外,此StackOverflow帖子还详细介绍了为什么要使用sharedApplication补救措施“创建多个应用程序”错误。

...这就是您所需要的!希望这有助于其他人。


flo*_*ger 5

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

应用程序代理

@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)
  }
}
Run Code Online (Sandbox Code Playgroud)

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")
    }
}
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

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

编辑:确保删除 Main.storyboard / MainMenu.xib,否则最终可能会在 UI 调试器中出现两个窗口。