[英]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.