简体   繁体   English

如何使嵌入式视图控制器成为响应者链的一部分?

[英]How to make embedded view controller part of the responder chain?

I am developing a Mac app using storyboards.我正在使用故事板开发 Mac 应用程序。 I have a window that presents an NSViewController as its contents, which contains a "container view controller" that embeds an NSSplitViewController .我有一个窗口,它显示一个NSViewController作为其内容,其中包含一个嵌入NSSplitViewController的“容器视图控制器”。

在此处输入图片说明

The expected behaviour is for the NSSplitViewController to be part of the responder chain, such that a menu item that triggers the toggleSidebar action on the first responder actually collapses the item of the NSSplitViewController that's marked as a sidebar.预期的行为是NSSplitViewController成为响应者链的一部分,这样在第一响应者上触发toggleSidebar操作的菜单项实际上会折叠标记为侧边栏的NSSplitViewController项。

However, this simply does not happen and the menu item remains disabled.但是,这根本不会发生并且菜单项保持禁用状态。 So my question is, how can-I get the NSSplitViewController to be part of the responder chain?所以我的问题是,如何让NSSplitViewController成为响应者链的一部分?

I noticed that maybe some of these solutions have worked, but i adapted a more general purpose answer from https://stackoverflow.com/a/30938725/6938357 .我注意到其中一些解决方案可能已经奏效,但我改编了来自https://stackoverflow.com/a/30938725/6938357的更通用的答案。

I made an extension on NSViewController to look for supplemental targets.我在NSViewController上做了一个扩展来寻找补充目标。 Works on NSSplitViewController as well as any general NSViewController with child(ren).适用于NSSplitViewController以及任何带有 child(ren) 的通用NSViewController

extension NSViewController {

    open override func supplementalTarget(forAction action: Selector, sender: Any?) -> Any? {
        if let target = super.supplementalTarget(forAction: action, sender: sender) {
            return target
        }

        for child in children {
            var target = NSApp.target(forAction: action, to: child, from: sender) as? NSResponder

            if target?.responds(to: action) == false {
                target = child.supplementalTarget(forAction: action, sender: sender) as? NSResponder
            }

            if target?.responds(to: action) == true {
                return target
            }
        }

        return nil
    }
}

If you only want this to search on a single view controller, put this implementation there instead.如果您只想在单个视图控制器上搜索它,请将这个实现放在那里。 This extension applies to all NSViewController s and its subclasses.此扩展适用于所有NSViewController及其子类。

Check out the nextReponsder property of NSResponder .退房nextReponsder财产NSResponder This property defines the responder chain.此属性定义响应者链。 It's normally set automatically to follow the responder change defined by the Cocoa framework, but you can alter it to insert/skip/divert the chain in a different direction.它通常会自动设置为遵循 Cocoa 框架定义的响应者更改,但您可以更改它以在不同方向插入/跳过/转移链。

For example, at some point (don't ask me when), Cocoa started including the window's controller in the responder chain.例如,在某些时候(不要问我什么时候),Cocoa 开始在响应者链中包含窗口的控制器。 So that my apps work consistently on all versions of macOS, I'll include code like this my window's controller:为了让我的应用程序在所有版本的 macOS 上一致运行,我将在我的窗口控制器中包含这样的代码:

- (void)windowDidLoad
{
    // Sent when the controller's window has been loaded from the nib
    [super windowDidLoad];
    NSWindow* window = self.window;

    // Make sure this window controller is in the responder chain
    NSResponder* nextResponder = window.nextResponder;  // get our window's next responder
    if (nextResponder!=self)
        {
        // running earlier OS X that does not include the window controller in the chain: patch us in
        self.nextResponder = nextResponder;
        window.nextResponder = self;
        }

-windowDidLoad , -viewDidLoad , and -awakeFromNib are all good places to adjust the responder chain so they include, or exclude, whatever objects you want. -windowDidLoad-viewDidLoad-awakeFromNib都是调整响应链的好地方,以便它们包括或排除您想要的任何对象。

I ended up getting this to work (in Swift 4) by adding my view controller to the window delegate.我最终通过将我的视图控制器添加到窗口委托来使其工作(在 Swift 4 中)。 After that, my view controller was part of the responder chain (which made application menu items work in my view controller).之后,我的视图控制器成为响应者链的一部分(这使得应用程序菜单项在我的视图控制器中工作)。

//Step 1: Add NSWindowDelegate to the controller
class MyViewController: NSViewController, NSWindowDelegate{
  override func viewDidLoad() {
    super.viewDidLoad()

    //Step 2: Add the view controller to the window delegate
    if let window = NSApp.windows.first{
      window.delegate = self
    }
  }
}

I hope that helps someone else.我希望能帮助别人。 :) :)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM