Not being able to edit NSTextField on NSPopover even though Editable behavior is set

∥☆過路亽.° 提交于 2019-11-26 20:22:14
Balazs Toth

Not sure if you still need the answer, but there may be some others still looking. I found a solution on apple developer forums. Quoting the original author:

The main problem is the way keyboard events works. Although the NSTextField (and all its superviews) receives keyboard events, it doesn't make any action. That happens because the view where the popover is atached, is in a window which can't become a key window. You can't access that window in any way, at least I couldn't. So the solution is override the method canBecomeKeyWindow for every NSWindow in our application using a category.

NSWindow+canBecomeKeyWindow.h
@interface NSWindow (canBecomeKeyWindow)

@end

NSWindow+canBecomeKeyWindow.m
@implementation NSWindow (canBecomeKeyWindow)

//This is to fix a bug with 10.7 where an NSPopover with a text field cannot be edited if its parent window won't become key
//The pragma statements disable the corresponding warning for overriding an already-implemented method
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
- (BOOL)canBecomeKeyWindow
{
    return YES;
}
#pragma clang diagnostic pop

@end

That makes the popover fully resposive. If you need another window which must respond NO to canBecomeKeyWindow, you can always make a subclass.

I struggled with this for a while as well, until I realized it was a bug.

However, instead of relying on an isActive state of a NSStatusItem view, I find it much more reliable to use the isShown property of the NSPopover you have implemented.

In my code, I have a NSPopover in a NSViewController:

  - (BOOL)canBecomeKeyWindow
    {
        if([self class]==NSClassFromString(@"NSStatusBarWindow"))
        {
            NSPopover *mainPopover = [[((AppDelegate*)[NSApp delegate]) mainViewController] mainPopover];
            if(![mainPopover isShown])
                return NO;
        }

        return YES;
    }

Balazs Toth's answer works, but if you're attaching the popover to NSStatusItem.view the status item becomes unresponsive - requiring two clicks to focus.

What i found when working with this solution is that when NSStatusItem becomes unresponsive, you can easily override this behavior like this

- (BOOL)canBecomeKeyWindow {
    if([self class]==NSClassFromString(@"NSStatusBarWindow")) {
        CBStatusBarView* view = [((CBAppDelegate*)[NSApp delegate]) statusItemView];
        if(![view isActive]) return NO;
    }
    return YES;
}

You will check for the class of the window, if it matches the NSStatusBarWindow we can then check somehow if the NSStatusItem is active. If it is, that means we have to return YES, because this way the NSPopover from NSStatusItem will have all keyboard events.

What I'm using for checking if the NSStatusItem was clicked (or is active) is that in my own custom view i have a bool value which changes when user clicks on the NSStatusItem, system automatically checks for "canBecomeKeyWindow" and when it does it will return NO and after user clicks on it (while it is returning the NO) it will change the bool value and return YES when system asks again (when NSPopover is being clicked for NSTextField editing).

Sidenotes:

  • CBStatusBarView is my custom view for NSStatusItem
  • CBAppDelegate is my App Delegate class

If anyone is still looking for an answer to this, I am working in Swift.

At the time where you wish the field to allow text entry, I have used myTextField.becomeFirstReponder()

To opt out; just use myTextField.resignFirstResponder()

Definitely a bug. That bug report is exactly what I was trying to do. Even down to creating the status item and overriding mousdown.

I can confirm that Balazs Toth's answer works. I just wonder if it might get in the way down the road.

If someone gets it and the solution above didn't do the trick for him. The problem in my app was in the info tab in the targets my application was set to

Application is background only = true

and shulde of been

 Application is agent = true

Spent an entire day on this thing.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!