从 NSCollectionView 获取当前焦点的文档对象

Get document object with current focus from NSCollectionView

我打开了一个(子类)NSCollectionView,其中包含多个文本视图。每个文本视图都映射到一个(子类)NSDocument 对象。 (想法是使用文档架构的保存功能而不是它的 windowing 功能,因为我需要在同一个 window 中使用多个文档,而传统的文档架构不允许这样做。)

现在,我希望用户能够从主菜单调用一个功能,该功能会影响他们当前选择的文档。也就是说:该文档当前在具有当前焦点的文本视图中可见,并且菜单命令应该对该文档进行更改。但是菜单命令的发送者只是菜单。当 window 控制器处理来自菜单的命令时,我如何告诉它当前选择的文档是什么?

这就是 the responder chain 的用途。

由于您正在使用 NSCollectionView,您可能已经拥有 NSCollectionViewItem 的子class。如果没有,请创建一个。在此 subclass 中实现您的操作方法。示例:

class DocumentItem: NSCollectionViewItem {

    var document: MyDocument? {
        return representedObject as? MyDocument
    }

    @IBAction func doThatThing(sender: AnyObject?) {
        Swift.print("This is where I do that thing to \(document)")
    }

    // @IBOutlets and whatnot here...

}

您可能需要在 xib 或故事板中将其设置为 NSCollectionViewItem 的自定义 class。

接下来,如果您的单元格视图(您的 NSCollectionViewItem 拥有的视图)已经不是 NSView 的自定义子class,您应该将其设为自定义子class。您必须将 acceptsFirstResponder 覆盖为 return true:

class DocumentCellView: NSView {

    override var acceptsFirstResponder: Bool { return true }

    // @IBOutlets and whatnot here...

}

确保在故事板或 xib 中将此设置为单元格视图的自定义 class。

最后,将菜单项的操作连接到 First Responder 上的 doThatThing:

工作原理如下:

因为单元格视图现在 return对于 acceptsFirstResponder 为真,当用户单击集合视图中的单元格视图时,系统会将其设为 第一响应者(响应者链的开始)。

当一个视图有一个视图控制器时,它使该视图控制器成为响应者链中其自身之后的下一个响应者(如果您使用 OS X 10.10 Yosemite 或更高版本)。您的单元格视图有一个视图控制器:您 return 来自 outlineView:itemForRepresentedObjectAtIndexPath: 的项目对象。 (NSCollectionViewItemNSViewController 的子 class,因此您的自定义项是视图控制器。)

当用户单击菜单项时,菜单项要求 NSApplication 沿着响应者链发送它的操作,从第一个响应者开始。第一响应者是单元格视图,但它不响应 doThatThing: 消息。所以 NSApplication 向视图询问它的 nextResponder,它是你的 NSCollectionViewItem subclass 的一个实例。该对象 确实 响应 doThatThing:,因此 NSApplicationdoThatThing: 发送到您的项目对象(使用 NSMenuItem 对象作为 sender 参数)并且不检查响应链的其余部分。