如何在 OSX Mavericks 上呈现 ViewControllerAsSheet?

how to presentViewControllerAsSheet on OSX Mavericks?

说来话长,言归正传;我的第一个 OSX 应用程序是在 Swift 中使用故事板编写的(在 Yosemite 上),直到我发现我的(完成的)应用程序不会 运行 在 Mavericks 上。我需要 运行 Mavericks,所以我用 NIB 替换了故事板。

我的问题是segues;我正在使用 'sheet type' segues 在主视图控制器上方的 sheet 中显示其他视图控制器。调用 NSViewController 的 presentViewControllerAsSheet 方法是一个很好的替代方法,因为它看起来一样,但是这个 API 是在 Yosemite 中引入的 - 所以我需要弄清楚如何为 Mavericks 执行此操作。

在主视图上的按钮操作中,我试过像这样使用 beginSheet:

secondViewController = SecondViewController(nibName: "SecondViewController", bundle: nil)
self.view.window?.beginSheet(secondViewController!view.window!, completionHandler: nil)

但是第二个视图控制器的 window 在 运行 时为空。我已经尝试将新的视图控制器作为子视图添加到应用程序 window 但这是一个无法识别的选择器:

NSApplication.sharedApplication().windows[0].addSubView(secondViewController!.view)

我到处搜索关于如何显示 sheet 的说明,我能找到的只有:Can a view controller own a sheet? 但很抱歉承认我不明白回答。任何人都可以帮我一些工作代码吗?我开始担心我正在尝试做一些不寻常的事情,但它在 Yosemite 上看起来不错,所以在 Yosemite 发布之前人们是怎么做到的?

编辑 我仍然没有得到解决方案,所以我整理了一个小应用程序来显示我遇到的问题。

在AppDelegate.swift中:

class AppDelegate: NSObject, NSApplicationDelegate {
    @IBOutlet weak var window: NSWindow!
    var mainViewController: FirstView!   
    func applicationDidFinishLaunching(aNotification: NSNotification) {
        mainViewController = FirstView(nibName:"FirstView", bundle: nil)
        window.contentView = mainViewController.view
        mainViewController.view.frame = (window.contentView as! NSView).bounds
    }
}

在 FirstView.swift 中(关联的笔尖有一个 'open sheet' 按钮)

class FirstView: NSViewController {
    var secondView: SecondView?
    var secondWindow: SecondWinCon?    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    @IBAction func pressButton(sender: AnyObject) {
        secondView = SecondView(nibName: "SecondView", bundle: nil)!
// method 1 - this is the behaviour I want (but it only works on OSX 10.10)
//        presentViewControllerAsSheet(secondView!)
// method 2 - this just creates a floating window
//        self.view.addSubview(secondView!.view)
//        self.view.window?.beginSheet(secondView!.view.window!, completionHandler: nil)
// method 3 - this also creates a floating window
        secondWindow = SecondWinCon(windowNibName: "SecondWinCon")
        self.view.window?.beginSheet(secondWindow!.window!, completionHandler: nil)   
    }    
}

在 SecondView.swift 中(关联的 NIB 有一个 'close' 按钮)

class SecondView: NSViewController {   
    override func viewDidLoad() {
        super.viewDidLoad()
    }       
    @IBAction func dismissPressed(sender: AnyObject) {
        if (presentingViewController != nil) {
            presentingViewController?.dismissViewController(self)
        } else {
            self.view.window?.sheetParent?.endSheet(self.view.window!)
        }
    }
}

在SecondWinCon.swift(关联NIB为空)

class SecondWinCon: NSWindowController {
    var secondView: SecondView?
    override func windowDidLoad() {
        super.windowDidLoad()   
        secondView = SecondView(nibName: "SecondView", bundle: nil)!
        self.window?.contentView.addSubview(secondView!.view)
    }   
}

如果取消注释方法 1,您将看到我试图模拟的行为(记住它只适用于 OS X 10.10)。方法 2 或 3 显示第二个视图,但不是 sheet。

如果您到这里来寻找解决方案,我几乎可以使用方法 3。我错过的重要步骤是关闭 NSWindowController 的 NIB(它是 NSWindow 的一个属性)中的 "Visible At Launch"。在我的示例代码中,这是在 SecondWinCon.nib 中。

我有同样的问题,发现可能不是与视图生命周期相关的问题。 当我在 viewDidLoad 中调用 presentViewControllerAsSheet 时,sheet 不会显示,您将在控制台中看到:

Failed to set (contentViewController) user defined inspected property on (NSWindow): presentViewController:animator:: View '''s view is not in a window/view hierarchy.

如果你在 viewWillAppear 或 viewDidAppear 中触发,完全没有问题。

更新

好吧,说清楚。

对于这个初始故事板,NSWindowController 与视图控制器相连,将其视为根视图控制器 (RootVC)。 在故事板 (SheetVC) 中创建另一个所需的视图控制器作为 sheet。 在 RootVC 的 viewWillAppear 或 viewDidAppear 中,[self presentViewControllerAsSheet: SheetVC]

将显示 sheet,无需额外代码。