【问题标题】:Document-based app where the top-level document represents the window, not tab基于文档的应用程序,其中顶级文档代表窗口,而不是选项卡
【发布时间】:2020-11-15 21:26:22
【问题描述】:

当我使用 Xcode 12 创建“文档应用程序”时,生成的应用程序模板是 CoreData 支持的“文档”代表当前 选项卡,如下所示:

所以基本上如果我点击 cmd-S,语义就是保存当前活动选项卡的内容。

但是,如果我希望“文档”代表该窗口中的所有选项卡,该怎么办?鉴于这些默认窗口选项卡是内置的,Cocoa 是否足够灵活以符合我的设计标准?

【问题讨论】:

  • 标签页被拖走形成一个新窗口时会变成什么?一个新的文件或仍然是相同的?一个文档上的所有选项卡是否都具有不同的视图。这个博客可能对机械师有帮助christiantietze.de/posts/2019/01/…
  • @WarrenBurton 如果一个选项卡从一个窗口拖到一个新窗口,它应该是它自己的新文档。不,每个选项卡都代表文档的“部分”,由选项卡所在的窗口表示。我想知道这是否可行,但这正是我所需要的。因此,如果您将选项卡从一个窗口拖到另一个窗口,则将其从第一个文档中删除并将其添加到第二个文档中。链接的资源(和后续帖子)看起来很棒,谢谢。我去看看。
  • 我认为可以在文档之间拖动窗口选项卡,但您想支持撤消、版本、状态恢复和/或其他花里胡哨吗?
  • @Willeke 如果可能的话是的,但我理解如果我没有根据我的特殊要求开箱即用这些东西。我猜我可以自己手动添加这样的功能。有关使初始功能正常工作的任何提示吗?

标签: macos cocoa document appkit


【解决方案1】:

您发布的示例在单个窗口内包含 多个 文档(UntitledUntitled 2、..)。这些选项卡中的每一个都是一个单独的文档,其选项卡式界面由 macOS 透明处理。

如果您想在单个文档中使用标签内部 - 例如Numbers 文档中的表格 - 您必须自己实现该功能。

【讨论】:

  • 是的,这就是我要问的。在我看来也是这样,但另一方面,Cocoa 的默认选项卡处理非常好(支持重新排序,甚至可以拖动以在新窗口中打开)。因此,需要自己重新实现所有这些(而且可能更糟)将是一种真正的耻辱。你 100% 是这样吗?
  • 基于文档的应用程序中的选项卡视图专为处理选项卡式文档而构建。您绝对需要构建自己的系统,以便在文档本身中使用选项卡式视图。
【解决方案2】:

移动窗口选项卡时,窗口的tabGroup 属性会发生变化。 tabGroup 属性是可观察的,但没有记录。可以使用addWindowController 将窗口控制器移动到另一个文档。我不知道如何防止新文档在另一个文档的选项卡中打开。访问tabGroup 似乎可行,但这是一个黑客。关闭和打开NSWindow.allowsAutomaticWindowTabbing 似乎也有效。这是我的测试应用程序(Xcode 应用程序模板,基于文档,XIB。添加一个 WindowController 子类并在 Document.xib 中更改文件所有者的类。)

class Document: NSDocument {

    var documentData: [String]? // String per window

    func addWindowTab(_ data: String?) {
        let windowController = WindowController(windowNibName: NSNib.Name("Document"))
        if let text = data {
            windowController.text = text
        }
        if let otherWindowController = windowControllers.first, // document has other window(s)
            let tabgroup = otherWindowController.window?.tabGroup,
            let newWindow = windowController.window {
            tabgroup.addWindow(newWindow)
        }
        else {
            _ = windowController.window?.tabGroup   // access tabGroup to open new documents in a new window
            windowController.showWindow(self)
        }
        addWindowController(windowController)
    }

    override func makeWindowControllers() {
        if let data = documentData {
            for windowData in data {
                addWindowTab(windowData)
            }
        }
        else {
            addWindowTab(nil)
        }
    }
    
    func windowControllerDidChangeTabGroup(_ windowController: NSWindowController) {
        var destinationDocument: Document?
        // check if the window is in a tabgroup with windows of another document
        if let tabGroup = windowController.window?.tabGroup,
            tabGroup.windows.count > 1 {
            for otherWindow in tabGroup.windows {
                if let otherWindowController = otherWindow.delegate as? WindowController,
                    otherWindowController.document !== self {
                    destinationDocument = otherWindowController.document as? Document
                    break;
                }
            }
        }
        // check if this document has other windows
        else if windowControllers.count > 1 {
            destinationDocument = Document()
            NSDocumentController.shared.addDocument(destinationDocument!)
        }
        if let destinationDocument = destinationDocument,
            destinationDocument !== self {
            destinationDocument.addWindowController(windowController) // removes windowController from self
            if windowControllers.count == 0 {
                close()
            }
        }
    }

}
class WindowController: NSWindowController {

    var tabObservation: NSKeyValueObservation?

    override func windowDidLoad() {
        super.windowDidLoad()
        tabObservation = window?.observe(\.tabGroup, options: []) { object, change in
            if let document = self.document as? Document {
                // accessing tabGroup can change tabGroup and cause recursion, schedule on runloop
                DispatchQueue.main.async{
                    document.windowControllerDidChangeTabGroup(self)
                }
            }
        }
    }
    
    override func newWindowForTab(_ sender: Any?) {
        if let document = document as? Document {
            document.addWindowTab(nil)
        }
    }
        
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多