Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Keep both NSSplitViewController's child controllers in first responder chain

Tags:

macos

cocoa

I've a document based app with an NSSplitViewController as the main window's content view controller. The left pane contains a custom view with controller, which implements some menu commands.

The right pane contains a standard NSTableView with controller. When the app starts the menu commands work as expected, but as soon as anything inside the right table view is selected, the menu commands get disabled.

How can I make sure that the view controller of the left pane remains inside the first responder chain?

I tried hooking up the menu commands directly to the correct view controller, but IB does not allow connections to another scene in a storyboard. I can only connect to objects in the same scene.

Regards,

Remco Poelstra

like image 649
Remco Poelstra Avatar asked Jun 19 '15 12:06

Remco Poelstra


2 Answers

Connect to First Responder.

You can have all child view controllers respond to actions by implementing -[NSResponder supplementalTargetForAction:sender:] in your NSSplitViewController subclass:

- (id)supplementalTargetForAction:(SEL)action sender:(id)sender
{
    id target = [super supplementalTargetForAction:action sender:sender];

    if (target != nil) {
        return target;
    }

    for (NSViewController *childViewController in self.childViewControllers) {
        target = [NSApp targetForAction:action to:childViewController from:sender];

        if (![target respondsToSelector:action]) {
            target = [target supplementalTargetForAction:action sender:sender];
        }

        if ([target respondsToSelector:action]) {
            return target;
        }
    }

    return nil;
}
like image 121
Pierre Bernard Avatar answered Nov 02 '22 07:11

Pierre Bernard


In Swift 4 you can do the following:

override func supplementalTarget(forAction action: Selector, sender: Any?) -> Any? {
    for childViewController in childViewControllers {
        if childViewController.responds(to: action) {

            return childViewController
        } else {
            guard let supplementalTarget = childViewController.supplementalTarget(forAction: action, sender: sender) else {
                continue
            }

            return supplementalTarget
        }
    }

    return super.supplementalTarget(forAction: action, sender: sender)
}
like image 32
Rafael Bugajewski Avatar answered Nov 02 '22 07:11

Rafael Bugajewski