NSApplicationDelegate在没有Storyboard

时间:2016-10-12 20:51:41

标签: swift xcode macos

我正在尝试在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)。任何帮助将不胜感激。

3 个答案:

答案 0 :(得分:15)

你需要在这里做一些事情

  1. 从plist
  2. 中删除NSMainStoryboardFile键/值
  3. 创建一个NSApplication子类并将其分配给Principal Class (NSPrincipalClass)键。
  4. assign custom class to principal class

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

    1. 在NSApplication子类中手动实例化您的委托,并将其分配给delegate属性。
    2. 确保对代理对象保持强引用。我刚刚在这里使用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开始任何重要操作。

答案 1 :(得分:6)

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

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

选项1:通过解决方法继续使用@NSApplicationMain

我发现您可以更改已接受答案的代码以解决该问题。这样做的方法是不要在名为super.init()的类中调用AppDelegate方法。

什么?

真。我认为有一个过于急切的断言来计算AppDelegate(或沿着这些行的某些逻辑)完成的次数,因此对super.init()的调用以及{override init()的完成都会被计算。 1}}阻止。这里有两种解决方法可供选择:

  1. 请勿拨打super.init() :这实际上是可行的completely healthy NSObject,至少在macOS中。但是,您无法在self区块中引用override init()

  2. 根本不要覆盖init() :考虑在applicationWillFinishLaunching(:)等生命周期方法中执行初始化过程。

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

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

    @NSApplicationMain只是一个我们可以近似的宏。幸运的是,我遇到了James H Fisher的blog post解释如何。我将在短时间内引用重要内容。

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

    无需更改Info.plist文件

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

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

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

    该文件必须被称为main.swift;它是special exception到斯威夫特&#34;表达式不允许在顶级&#34;规则。

    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课程,并将其指定为.delegate myApp。您现在可以看到默认项目选择调用类AppDelegate的原因:它被设置为.delegate上的NSApplication

         

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

    另外this StackOverflow post详细介绍了为什么使用sharedApplication补救&#34;创建多个应用程序&#34;错误。

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

答案 2 :(得分: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。