我有一个NSTokenField,我在popover中提供自定义自动完成功能。不幸的是,一旦弹出窗口显示它就迫使NSTokenField辞职第一响应者。有没有办法显示弹出窗口而不是松开NSTokenField上的第一个响应者?
覆盖NSTokenField的resignFirstResponder会导致NSTokenField停止工作(它不接受任何击键)。覆盖NSPopover的acceptedFirstResponder方法或按this问题中的建议设置其行为也不起作用。
编辑:NSTokenField不在NSPopover的contentViewController.view中。 Edit2:像Mailapp中搜索栏的行为将是最佳解决方案。
答案 0 :(得分:9)
不幸的是,没有干净的方法可以做到这一点。幸运的是,我在Delicious Library 3中做了一个丑陋的方法 - 你需要把这个方法放在NSWindow的子类中,并确保有问题的文档窗口是那个子类:
- (BOOL)makeFirstResponder:(NSResponder *)responder;
{
// Prevent popover content view from forcing our current first responder to resign
if (responder != self.firstResponder && [responder isKindOfClass:[NSView class]]) {
NSWindow *const newFirstResponderWindow = ((NSView *)responder).window;
NSWindow *currentFirstResponderWindow;
NSResponder *const currentFirstResponder = self.firstResponder;
if ([currentFirstResponder isKindOfClass:[NSWindow class]])
currentFirstResponderWindow = (id)currentFirstResponder;
else if ([currentFirstResponder isKindOfClass:[NSView class]])
currentFirstResponderWindow = ((NSView *)currentFirstResponder).window;
// Prevent some view in popover from stealing our first responder, but allow the user to explicitly activate it with a click on the popover.
// Note that the current first responder may be in a child window, if it's a control in the "thick titlebar" area and we're currently full-screen.
if (newFirstResponderWindow != self && newFirstResponderWindow != currentFirstResponderWindow && self.currentEvent.window != newFirstResponderWindow)
for (NSView *responderView = (id)responder; responderView; responderView = responderView.superview)
if ([responderView conformsToProtocol:@protocol(LIPopoverFirstResponderStealingSuppression)] &&
((id <LIPopoverFirstResponderStealingSuppression>)responderView).suppressFirstResponderWhenPopoverShows)
return NO;
}
return [super makeFirstResponder:responder];
}
现在确保popover的内容视图子类实现此协议:
// NSPopover doesn't respect -acceptsFirstResponder of its content view (Radar 10666891).
@protocol LIPopoverFirstResponderStealingSuppression <NSObject>
@property (readonly, nonatomic) BOOL suppressFirstResponderWhenPopoverShows;
@end
请同时向Apple提交错误请求NSPopover尊重-acceptsFirstResponder的内容视图; 100%的情况是,当多个开发人员提交错误时,他们会得到修复。
答案 1 :(得分:0)
制作popover内容的子类(文本视图?)并实现-(void)canBecomeKeyView
。那边还没有。当弹出窗口显示时,它只被调用一次,因此您仍然可以与它进行交互,但它不会再盗取第一个响应者状态。
答案 2 :(得分:0)
我修改了Will对Swift的回答:
protocol PopoverFirstResponderStealingSuppression {
var suppressFirstResponderWhenPopoverShows: Bool { get }
}
class TTWindow: NSWindow {
override func makeFirstResponder(_ responder: NSResponder?) -> Bool {
if responder != firstResponder, let responderView = responder as? NSView {
// Prevent popover content view from forcing our current first responder to resign
let newFirstResponderWindow = responderView.window!
var currentFirstResponderWindow: NSWindow? = nil
let currentFirstResponder = firstResponder
if let currentFirstResponder = currentFirstResponder as? NSWindow {
currentFirstResponderWindow = currentFirstResponder
}
else if let currentFirstResponder = currentFirstResponder as? NSView {
currentFirstResponderWindow = currentFirstResponder.window
}
// Prevent some view in popover from stealing our first responder, but allow the user to explicitly activate it with a click on the popover.
// Note that the current first responder may be in a child window, if it's a control in the "thick titlebar" area and we're currently full-screen.
if newFirstResponderWindow != self, newFirstResponderWindow != currentFirstResponderWindow, currentEvent?.window != newFirstResponderWindow {
var currentView: NSView? = responderView
while currentView != nil {
if let currentView = currentView as? PopoverFirstResponderStealingSuppression, currentView.suppressFirstResponderWhenPopoverShows {
return false
}
currentView = currentView?.superview
}
}
}
return super.makeFirstResponder(responder)
}
}
答案 3 :(得分:0)
在设计要在表视图中使用的自定义控件时,我偶然发现了这个问题。
看来,至少在最新版本的Mac OS X中,您可以将表格发送到Popover(或那里的任何窃取焦点的视图)中tableView.refusesFirstResponder = true
。
如果您这样做,弹出窗口将不再尝试窃取第一响应者。