从AppDelegate连接到ViewController(Swift)

9

我使用标准的Xcode Swift模板(使用StoryBoards)创建了一个新的OS X Cocoa应用程序。

我在AppDelegate.swift中实现了一个IBAction来处理用户从“文件”菜单中选择“打开...”时的操作。如果所选文件是有效的图像文件,则我创建一个NSImage,然后希望在ViewController的视图中显示它。

    @IBAction func openFile(sender: NSMenuItem) {
    var openPanel = NSOpenPanel()
    openPanel.beginWithCompletionHandler { (result :Int) -> Void in
        if result == NSFileHandlingPanelOKButton {
            if let imageURL = openPanel.URL {
                let image = NSImage(contentsOfURL: imageURL)
                // PRESENT image IN THE VIEW CONTROLLER
            }
        }
    }

然而,我没有找到任何方法可以从AppDelegate连接到ViewController。我只能找到建议,在AppDelegate中查看self.window!但是在AppDelegate中并没有窗口。

谢谢, Michael Knudsen

5个回答

18
似乎AppDelegate只能连接故事板中的应用场景内的对象。如果您想获取ViewController,请从故事板中实例化它。
示例:
@IBAction func menuAction(sender: AnyObject) {
    if let storyboard = NSStoryboard(name: "Main", bundle: nil) {
        let controller = storyboard.instantiateControllerWithIdentifier("VC1") as NSViewController

        if let window = NSApplication.sharedApplication().mainWindow {
            window.contentViewController = controller // just swap
        }
    }
}

谢谢!正是我所需要的! - Michael Knudsen
一旦我有了一个实例,我该如何使用它?我尝试直接调用函数,但这并不起作用(或者说,它期望参数在我的方法声明中没有定义)。我想这种分离有一些原因,但由于应用程序菜单只能调用AppDelegate(而菜单是UI的一部分),所以我不明白为什么会这样。是否有一种简单和/或正确的方法让我的菜单项更新我的视图? - Subcreation
1
使用第一响应者怎么样?在Storyboard中,将菜单的操作连接到您定义为@IBAction的第一响应者的操作。但是NSViewController不在响应链中,因此请将您的控制器设置为窗口的代理(NSWindow在响应链中)。 - bluedome

4

您可以访问mainWindow属性和contentViewController属性,以创建对自定义ViewController类的引用。这类似于iOS的rootViewController属性。

let rootViewController = NSApplication.shared().mainWindow?.windowController?.contentViewController as! ViewController

现在你可以使用这个引用来从你的AppDelegate访问主storyboard上的IBOutlets。
rootViewController.myTextView.textStorage?.mutableString.setString("Cats and dogs.")

这对于只有一个窗口和一个视图控制器的简单应用程序来说非常好。


不好意思,但是通过NSApplication.shared()从AppDelegate访问根视图控制器的引用?这听起来不太对。 - El Tomato
@ElTomato shared() 可以使用无括号符号表示法。 例如) NSApplication.shared.mainWindow?.windowController?.contentViewController as! ViewController - Namo

1
我最近尝试做同样的事情时遇到了困难,通过在我的ViewController中创建@IBAction并将其拖动到应用程序的First Responder(在故事板视图中的菜单上方)中,我成功获取了所需的事件以更新我的视图。
这是让我走出困境的问题: Xcode中的应用程序菜单项 感谢Bluedome建议将其连接到First Responder的操作。

0
如果你从菜单中控制拖动到第一响应者(菜单上面的红色立方体)并选择一个现有操作,那么你可以“响应者链”到你的视图控制器。在我的情况下,我附加了打开文件的Open,并在我的视图控制器中添加了以下内容。
override var acceptsFirstResponder: Bool {
    return true
}

func openFile(sender: NSMenuItem) {
    print("In view controller")
}

而且它在AppDelegate中没有进行任何更改就可以正常运行。大部分菜单已经与第一响应者连接起来,所以只需在您的视图控制器中添加匹配的函数名称。

有关更多信息,请参阅评论事件处理基础知识中的文档。


0

在Swift 5中访问新窗口数组:

@IBAction func menuAction(sender: AnyObject) {
    let storyboard = UIStoryboard(name: "Main", bundle: nil)
    let controller = storyboard.instantiateInitialViewController()
    // The windows in the array are ordered from back to front by window level;
    // thus, the last window in the array is on top of all other app windows.
    // On app launch, UIApplication.shared.windows.count == 1 anyway.
    if let window = UIApplication.shared.windows.last {
        window.rootViewController = controller
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接