NSTextField не замечает потерянный фокус при нажатии Tab? - PullRequest
2 голосов
/ 07 июля 2010

Кажется, я не могу найти способ получать уведомления, когда NSTextField теряет фокус, нажав клавишу Tab. Я получаю хороший textDidEndEditing при нажатии другого элемента управления или при нажатии клавиши Enter, но не при изменении фокуса нажатием клавиши Tab.

Также пытался дергать KeyDown и doCommandBySelector для этой цели, но у меня ничего не получилось.

Есть идеи?

Заранее спасибо

Edit:

Забыл упомянуть, но я тоже попробовал resignFirstResponder. Это код, который я пробовал:

- (BOOL)resignFirstResponder
{
    NSRunAlertPanel(@"", @"Lost Focus",@"OK", nil, nil);
    return [super resignFirstResponder];
}
- (BOOL)becomeFirstResponder
{
    NSRunAlertPanel(@"", @"Got focus",@"OK", nil, nil);
    return [super becomeFirstResponder];
}

Странно, что здесь происходит то, что при получении фокуса, становиться и становиться, и становиться так же, как и становиться первым, и становиться называться. Но при изменении фокуса от контроля, ни один из них.

Ответы [ 7 ]

6 голосов
/ 03 апреля 2011

"Я получаю хороший textDidEndEditing при нажатии другого элемента управления или при нажатии клавиши Enter, но не при изменении фокуса нажатием клавиши Tab."

По состоянию на апрель 2011 г.Libs OS X 10.6, я использую:

- (void)controlTextDidEndEditing:(NSNotification *)aNotification

... чтобы прослушать потерю фокуса NSTextField, и он работает правильно.Возможно ли это в вашей ситуации?Это что-то, что раньше было сломано, но теперь исправлено Apple?

Если так, то кода намного меньше :).

2 голосов
/ 04 декабря 2013

Вы должны сделать только это

Для клавиши Tab

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

или посмотрите мой ответ на Выполнить действие при нажатии клавиши ввода в NSTextField?

2 голосов
/ 13 июля 2010

Хорошо, я нашел способ сделать это: использовать делегат окна, чтобы заставить окно возвращать редактор пользовательских полей. Этот редактор полей отслеживает последнее активированное TextField и вызывает его метод textDidEndEditting при потере самого firstResponder. Вот пример того, как это сделать:

#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;
}
0 голосов
/ 16 июля 2015

Комплексные ответы. Есть более простой способ сделать это.

Не забудьте создать подкласс 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
0 голосов
/ 16 февраля 2013

Вот пример того, как указать подходящее время, когда пользовательский NSTextFieldCell (NSCell) должен нарисовать свое собственное кольцо рамки и фокуса (в методе [NSTextFieldCell drawWithFrame: inView]), заимствуя поле выделения ячейки, устанавливая его когда текстовое поле получает фокус, и очищает его, когда текстовое поле теряет фокус (редактирование завершено).

Эта техника преодолевает некоторые проблемы:

  1. Ячейка не может легко определить, имеет ли она фокус.
  2. Ячейка не может легко определить, какому компоненту более высокого уровня (например, текстовому полю или кнопке) она принадлежит для отслеживания через своего родителя
  3. NSTextField может мгновенно подать в отставку первого респондента после его получения, что может создать впечатление, что он потерял фокус пользователя, когда он этого не сделал.

Поскольку мы переделываем поле «выделенное» для ячейки, чтобы сообщить о состоянии фокуса ячейке, обязательно верните nil из пользовательского метода [highlightColorWithFrame: inView:] метода NSTextFieldCell.

#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
0 голосов
/ 07 июля 2010

С пониманием, которое я упомянул в своем другом посте, я понял ответ.Это немного запутанно, но это работает.Вы должны создать подкласс как 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

Сделайте классы и сделайте ваши объекты их классом.Вы будете уведомлены о первом изменении респондента из вашего текстового поля, где сообщение NSLog находится в файле HMWindow.m.Если вам нужна помощь, чтобы понять, как это работает, дайте мне знать.

0 голосов
/ 07 июля 2010

NSTextField является подклассом NSResponder. У NSResponder есть метод - (BOOL) resignFirstResponder. Это уведомит вас, когда NSTextField больше не будет первым респондентом ... т.е. теряет фокус Так что создайте подкласс вашего NSTextField и делайте там свои вещи.

...