6

我有一个 NSTokenField 并且我在弹出框内提供了一个自定义的自动完成功能。不幸的是,一旦显示弹出框,它就会强制 NSTokenField 辞职第一响应者。有没有办法显示弹出窗口但不松开 NSTokenField 上的第一响应者?

覆盖 NSTokenField 的 resignFirstResponder 会导致 NSTokenField 停止工作(它不接受任何击键)。覆盖 NSPopover 的 acceptFirstResponder 方法或按照此问题中的建议设置其行为也不起作用。

编辑:NSTokenField 不在 NSPopover 的 contentViewController.view 中。Edit2:Mailapp 中的搜索栏等行为将是最佳解决方案。 邮件应用程序

弹出框

4

4 回答 4

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];
}

现在确保弹出框的内容视图子类实现了这个协议:

// NSPopover doesn't respect -acceptsFirstResponder of its content view (Radar 10666891).
@protocol LIPopoverFirstResponderStealingSuppression <NSObject>
@property (readonly, nonatomic) BOOL suppressFirstResponderWhenPopoverShows;
@end

还请向 Apple 提交错误以请求 NSPopover 尊重 -acceptsFirstResponder 的内容视图;100% 的情况是,当多个开发人员提交错误时,他们会得到修复。

于 2014-01-14T10:40:41.857 回答
2

在设计要在表格视图中使用的自定义控件时,我碰巧偶然发现了这个问题。

似乎至少在最新版本的 Mac OS X 中,您可以将表格发送到您的 Popover(或您在那里获得的任何焦点窃取视图)中tableView.refusesFirstResponder = true

如果您这样做,弹出框将不再尝试窃取第一响应者。

于 2019-02-15T21:45:23.133 回答
0

制作弹出框内容的子类(文本视图?)并实现-(void)canBecomeKeyView. 在那里返回 NO。它只在显示弹出框时调用一次,因此您仍然可以与之交互,但它不再窃取第一响应者的状态。

于 2014-06-21T09:56:45.857 回答
0

我改编了威尔对斯威夫特的回答:

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)
    }
}
于 2018-07-18T18:03:43.743 回答