按 T​​ab 时 NSTextField 没有注意到失去焦点?

Posted

技术标签:

【中文标题】按 T​​ab 时 NSTextField 没有注意到失去焦点?【英文标题】:NSTextField not noticing lost focus when pressing Tab? 【发布时间】:2010-07-07 08:03:51 【问题描述】:

当 NSTextField 通过按 Tab 键失去焦点时,我似乎找不到获得通知的方法。单击另一个控件或按 Enter 时,我会得到一个不错的 textDidEndEditing,但如果我通过按 Tab 键更改焦点则不会。

还为此目的尝试拉出 KeyDowndoCommandBySelector,但我一无所获。

有什么想法吗?

提前致谢

编辑:

忘了提,但我也试过 resignFirstResponder。这是我试过的代码:

- (BOOL)resignFirstResponder

    NSRunAlertPanel(@"", @"Lost Focus",@"OK", nil, nil);
    return [super resignFirstResponder];

- (BOOL)becomeFirstResponder

    NSRunAlertPanel(@"", @"Got focus",@"OK", nil, nil);
    return [super becomeFirstResponder];

奇怪的是,这里发生的情况是,当获得焦点时,becomeFirstResponder 和 resignFirstResponder 都被一个接一个地调用。但是当将焦点从控件上移开时,两者都不是。

【问题讨论】:

【参考方案1】:

“当我收到一个不错的 textDidEndEditing 时 单击另一个控件或何时 按 Enter,但如果我更改则不会 按 T​​ab 键获得焦点。”

截至 2011 年 4 月,我正在使用 OS X 10.6 库:

- (void)controlTextDidEndEditing:(NSNotification *)aNotification

...监听 NSTextField 失去焦点,它工作正常。在你的情况下这可能吗?是不是以前坏了,现在被 Apple 修复了?

如果是这样,那就少了很多代码:)。

【讨论】:

使用 10.7 SDK、10.6 部署目标对我来说也很好。【参考方案2】:

好的,我找到了一种方法:使用窗口委托使窗口返回自定义字段编辑器。此字段编辑器跟踪最后一个被激活的 TextField 并在丢失 firstResponder 本身时调用其 textDidEndEditting 方法。这是一个如何做到这一点的例子:

#import <Cocoa/Cocoa.h>
#import <AppKit/AppKit.h>


@interface MyTextField : NSTextField
- (BOOL)resignFirstResponder;
- (void)textDidEndEditing:(NSNotification *)notification;
@end

@interface MyFieldEditor : NSTextView

    MyTextField * lastBox;

-(void) setLastEditBox:(MyTextField*) box;
@end

@interface MyWindowDelegate : NSWindowController 

    MyFieldEditor *fieldEditor;

@end



@implementation MyFieldEditor

-(void) setLastEditBox:(MyTextField*) box lastBox = box; 

-(id)init

    if (self = [super init]) 
        [self setFieldEditor:YES];

    return self;


- (BOOL)resignFirstResponder

    // Activate the last active editbox editting-end event
    if(lastBox != nil)
    
        [lastBox textShouldEndEditing:self];
        lastBox = nil;
    

    return [super resignFirstResponder];


@end


@implementation MyWindowDelegate

-(id)windowWillReturnFieldEditor:(NSWindow *)sender toObject:(id)client

    if(fieldEditor == nil)  // Return our special field editor
        fieldEditor = [[[MyFieldEditor alloc] autorelease] init];
    return fieldEditor;

@end


@implementation MyTextField

- (BOOL)resignFirstResponder

    // We're losing first responder, inform the field editor that this was the last edit box activated
    MyFieldEditor* myTf = (MyFieldEditor*) [[self window] fieldEditor:YES forObject:self];
    [myTf setLastEditBox:self];
    return [super resignFirstResponder];


- (void)textDidEndEditing:(NSNotification *)notification;

    [super textDidEndEditing:notification];
    [self setStringValue:@"RECEIVED ENDEDITING"];


@end




int main(int argc, char *argv[])
   
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSApplication *app = [NSApplication sharedApplication];

    NSRect frame = NSMakeRect(100, 100, 200, 150);

    // Create the window
    NSWindow* window  = [[[NSWindow alloc] autorelease ] initWithContentRect:frame styleMask:NSClosableWindowMask|NSResizableWindowMask
                                                      backing:NSBackingStoreBuffered defer:NO];

    [window setDelegate:[[MyWindowDelegate alloc] autorelease]];


    MyTextField * tf = [ [[ MyTextField alloc ] autorelease] initWithFrame: NSMakeRect( 30.0, 100.0, 150.0, 22.0 ) ];
    [ [ window contentView ] addSubview: tf ];

    MyTextField * tf2 = [ [[ MyTextField alloc ] autorelease] initWithFrame: NSMakeRect( 30.0, 40.0, 150.0, 22.0 ) ];
    [ [ window contentView ] addSubview: tf2 ];

    [window makeKeyAndOrderFront: window];  

    [app run];
    [pool release];

    return 0;

【讨论】:

【参考方案3】:

你只需要这样做

键标签

self.textfield.delegate = self;

然后实现这个方法

- (void)control:(NSControl *)control textView:(NSTextView *)fieldEditor doCommandBySelector:(SEL)commandSelector

    NSLog(@"Selector method is (%@)", NSStringFromSelector( commandSelector ) );
    if (commandSelector == @selector(insertTab:)) 
        //Do something against TAB key
        //Or Call a Method
    
 

或查看我的答案 Execute an Action when the Enter-Key is pressed in a NSTextField?

【讨论】:

当我实现这个时,escbackspace 等按钮停止工作。您实际上不需要实现委托方法。只需将您的文本字段连接到界面生成器中的委托,并确保未选中 Refuses First Responder【参考方案4】:

根据我在另一篇文章中提到的理解,我想出了一个答案。这有点令人费解,但它有效。您必须将 NSTextField 和 NSWindow 子类化,因为您需要两者的信息来设置它。这是子类: HMTextField.h

#import <Foundation/Foundation.h>

@interface HMTextField : NSTextField 



@end

HMTextField.m

#import "HMTextField.h"
#import "HMWindow.h"

@implementation HMTextField

- (BOOL)becomeFirstResponder 
    [(HMWindow*)[self window] setTfBecameFirstResponder:YES];
    return [super becomeFirstResponder];


@end

HMWindow.h

#import <Foundation/Foundation.h>

@interface HMWindow : NSWindow 
    BOOL tfIsFirstResponder, tfBecameFirstResponder;


@property (nonatomic, readwrite, assign) BOOL tfBecameFirstResponder;

@end

HMWindow.m

#import "HMWindow.h"

@implementation HMWindow

@synthesize tfBecameFirstResponder;

-(id)init 
    if (self = [super init]) 
        tfIsFirstResponder = NO;
    
    return self;


- (NSResponder *)firstResponder 
    id fr = [super firstResponder];

    if ([fr isEqualTo:[self fieldEditor:NO forObject:nil]]) 
        tfIsFirstResponder = YES;
     else 
        if (tfIsFirstResponder && tfBecameFirstResponder) 
            NSLog(@"the text field stopped being first responder");
            tfBecameFirstResponder = NO;
        
        tfIsFirstResponder = NO;
    

    return fr;


@end

创建类并让你的对象成为它们的类。您将在 HMWindow.m 文件中的 NSLog 消息所在的文本字段中收到第一响应者更改的通知。如果您需要帮助了解它的工作原理,请告诉我。

【讨论】:

问题是,这是针对具有一堆 TextField、spinbox 和其他东西的交互式应用程序,并且每次用户停止修改控件时它都需要更新视图。也就是说,当 textDidEndEditing 事件触发时,我们会更新视图。这很好用,只是在使用 Tab 键更改焦点时不会触发此事件。这意味着我们需要在每个窗口中支持多个文本字段,并且我们需要在焦点更改发生时收到事件通知,而不是像使用此解决方案那样查询它:-S 无论如何感谢您的建议【参考方案5】:

这是一个示例,说明如何指示自定义 NSTextFieldCell (NSCell) 绘制自己的边框和焦点环(在方法 [NSTextFieldCell drawWithFrame: inView] 中)的适当时间,方法是“借用”单元格的突出显示字段,设置它当文本字段获得焦点时,并在文本字段失去焦点时清除它(编辑完成)。

这种技术克服了一些问题:

    单元格无法轻易确定是否有焦点。 单元格无法通过其父级轻松确定它属于哪个更高级别的组件(例如文本字段或按钮)来跟踪 NSTextField 可以在获得第一响应者后立即辞职,这可能使它看起来好像失去了用户焦点而实际上没有。

由于我们重新利用单元格的“突出显示”状态字段,为了将焦点状态传达给单元格,请务必从自定义 NSTextFieldCell 的 [highlightColorWithFrame: inView:] 方法返回 nil。

#import "CustomTextField.h"

@implementation CustomTextField

-(BOOL)becomeFirstResponder 
    ((NSTextFieldCell *)self.cell).highlighted = true;
    return [super becomeFirstResponder];


 -(void)textDidEndEditing:(NSNotification *)notification 
    ((NSTextFieldCell *)self.cell).highlighted = false;
    [super textDidEndEditing:notification];
 
@end

【讨论】:

【参考方案6】:

复杂的答案。有一种更简单的方法。

不要忘记将您的 NSTextField 子类化为 NotificableTextField 并将其委托设置为您的视图控制器。

NotificableTextField.h

#import <Cocoa/Cocoa.h>

@protocol NotificableTextFieldDelegate <NSObject>
@optional
- (void)textFieldStartedEditing:(NSTextField *)textField;
- (void)textFieldEndedEditing:(NSTextField *)textField;
@end

@interface NotificableTextField : NSTextField
@end

NotificableTextField.m

#import "NotificableTextField.h"

@implementation NotificableTextField

- (void)awakeFromNib

    [super awakeFromNib];

    self.target = self;
    self.action = @selector(inputEnd);


- (BOOL)becomeFirstResponder

    BOOL status = [super becomeFirstResponder];
    if (status && [self.delegate respondsToSelector:@selector(textFieldStartedEditing:)])
        [(id<NotificableTextFieldDelegate>)self.delegate textFieldStartedEditing:self];
    return status;


- (void)inputEnd

    if ([self.delegate respondsToSelector:@selector(textFieldEndedEditing:)])
        [(id<NotificableTextFieldDelegate>)self.delegate textFieldEndedEditing:self];


@end

【讨论】:

【参考方案7】:

NSTextField 是 NSResponder 的子类。 NSResponder 有一个方法 - (BOOL)resignFirstResponder。当 NSTextField 不再是第一响应者时,它将通知您......即。失去焦点。所以继承你的 NSTextField 并在那里做你的事情。

【讨论】:

查看更新后的帖子。我试过了,但它的行为不像预期的那样 我现在明白为什么了。一个窗口有一个字段编辑器,用于窗口中所有需要文本的对象。当您键入内容时,您实际上是在字段编辑器中输入它,而不是文本字段本身。因此,文本字段成为第一响应者,然后立即辞去第一响应者的职务,因为字段编辑器成为第一响应者。

以上是关于按 T​​ab 时 NSTextField 没有注意到失去焦点?的主要内容,如果未能解决你的问题,请参考以下文章

将热键添加到 NSTextField

Mac NSTextField 不会辞职 firstResponder

NSManagedObjectContext 保存导致 NSTextField 失去焦点

如何获取在 Xamarin MacOS 中以编程方式创建的 NSTextField 的值?

鼠标离开 NSWindow 时 NSTextField 失去焦点/FirstResponder

Cocoa NSTextField 拖放需要子类...真的吗?