如何在macOS下的非基于文档的应用程序中添加选项卡?

DaP*_*hil 7 macos xcode tabs

我喜欢使用Xcode 8在Swift 3中构建一个应用程序,它应该启用Apple标签栏.它不是基于文档的.我在这里学到,如果我@IBAction func newWindowForTab(_ sender: Any?)在窗口控制器中覆盖方法,则可以启用选项卡.为了测试这个,我使用storyboard在Xcode中创建了一个新项目,添加了一个子类NSWindowController并在故事板中分配它.然后我实施了

@IBAction override func newWindowForTab(_ sender: Any?) {}
Run Code Online (Sandbox Code Playgroud)

并在构建应用程序时显示标签栏.重建之后,我注意到只有在构建之前关闭应用程序时标签栏不可见时才会出现"+" - 按钮.那是一个错误吗?我该如何添加新标签?

cti*_*tze 8

从概念上讲,这就是发生的事情:

  • 只需调用NSWindow.addTabbedWindow(_:ordered:)将窗口添加到本机选项卡栏即可。
  • 放入NSResponder.newWindowForTab(_:)主窗口的响应者链后,“+”按钮将可见。
  • 设置后window.tabbingMode = .preferred,标签栏将始终可见。

但是,在实施这些方法时有一些注意事项。

实施 newWindowForTab

那么在哪里添加@IBAction override func newWindowForTab(_ sender: Any?)以便您可以调用NSWindow.addTabbedWindow(_:ordered:)

  • 如果您使用 Storyboards,则将其放入NSWindowController您拥有的子类中。这是NSWindow调用的最简单方法addTabbedWindow
  • 如果您使用 Xibs,AppDelegate将引用主窗口。你可以把方法放在这里。
  • 如果您使用编程设置,请将其放在您知道主NSWindow实例的任何位置。

使“+”按钮起作用

TL;DR:当您初始化一个新窗口时,将窗口存储在windowController某处。您需要维护一个强引用,以便处理窗口事件(在控制器中)。

我用 TabManager 编写了一个示例应用程序来处理这个问题:https : //github.com/DivineDominion/NSWindow-Tabbing

以及详细的博客文章:https : //christiantietze.de/posts/2019/07/nswindow-tabbing-multiple-nswindowcontroller/

考虑事件的调度方式。主菜单消息被发下来的响应链,所以是newWindowForTabNSApp.sendAction如果调用源没有一直连接到标准事件,则标准事件将失败 - 这意味着,至少取决于您的NSWindowController,甚至可能取决于您的AppDelegate.

您必须确保添加的任何其他窗口实际上与原始窗口属于同一响应程序链,否则菜单项将停止工作(并变灰/禁用)。同样,当您单击“+”按钮时,它会停止工作。

这就是@JohnV 在另一个答案的评论中所说的:“没有 subview 变量,您不能创建两个以上的选项卡”。这就是效果,但这不是真正的解释。您始终可以创建更多选项卡,但只能从原始窗口/选项卡创建,而不是新窗口/选项卡;那是因为另一个选项卡没有响应newWindowForTab.

“另一个标签”本身只是一个NSWindow. 不过,您的newWindowForTab实现驻留在控制器中。那是上一层。

修改@Peter Ahlberg 的代码,这将起作用:

class WindowController: NSWindowController {

    @IBAction override func newWindowForTab(_ sender: Any?) {

        let windowController: WindowController = self.storyboard?.instantiateInitialController() as! WindowController
        let newWindow = windowController.window

        self.window?.addTabbedWindow(newWindow, ordered: .above)

        newWindow.orderFront(self.window)
        newWindow.makeKey()

        // Store the windowController in a collection of sorts
        // to keep a strong reference and make it handle events:
        // (NSApp.delegate as? AppDelegate).addManagedWindowController(windowController)
    }
}
Run Code Online (Sandbox Code Playgroud)

我不需要添加newWindowForTabtoAppDelegate来使用 Storyboards 使一切正常工作——因为这样窗口控制器就可以继续工作并且不需要回退!


Pet*_*erg 6

好的,这是新文件,

的appdelegate

import Cocoa

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

func applicationDidFinishLaunching(_ aNotification: Notification) {
    // Insert code here to initialize your application
}

func applicationWillTerminate(_ aNotification: Notification) {
    // Insert code here to tear down your application
}

func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
    return true
}

@IBAction func newWindowForTab(_ sender: Any?){
} // without this the + button doesn't show from start

}
Run Code Online (Sandbox Code Playgroud)

视图控制器

import Cocoa

class ViewController: NSViewController {

override func viewDidLoad() {
    super.viewDidLoad()

    // Do any additional setup after loading the view.
}

override var representedObject: Any? {
    didSet {
    // Update the view, if already loaded.
    }
}
}
Run Code Online (Sandbox Code Playgroud)

和WindowController

import Cocoa

class WindowController: NSWindowController {

var subview: WindowController?

override func windowDidLoad() {
    super.windowDidLoad()
    // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
}

@IBAction override func newWindowForTab(_ sender: Any?) {

    let story = self.storyboard
    let windowVC: WindowController = story?.instantiateInitialController() as! WindowController

    self.window?.addTabbedWindow(windowVC.window!, ordered: .above)
    self.subview = windowVC

    windowVC.window?.orderFront(self.window)
    windowVC.window?.makeKey()
}

}
Run Code Online (Sandbox Code Playgroud)

你必须添加菜单项并将它连接到菜单视图中的FirstResponder到newWindowForTab:action,assign key,比如说cmd + t才能工作,这个例子只是从+按钮和窗口菜单选项中添加标签工作,"将标签移动到新的窗口"和"合并所有窗口".您可以拖出标签并放回,水平移动标签.看起来很有效.

完成Xcode版本8.2 beta(8C30a)

  • 为什么要将新加载的 `windowVC` 存储到 `subview` 变量中?只是好奇。 (2认同)