Как определить, когда NSTextField имеет фокус или его содержимое выбрано какао

У меня есть NSTextField внутри NSTableCellView, и я хочу событие, которое информирует меня, когда мой NSTextField получил фокус для отключения нескольких кнопок, я нашел этот метод:

-(void)controlTextDidBeginEditing:(NSNotification *)obj{
    NSTextField *textField  = (NSTextField *)[obj object];

    if (textField != _nombreDelPaqueteTextField) {
        [_nuevaCuentaActivoButton   setEnabled:FALSE];
        [_nuevaCuentaPasivoButton   setEnabled:FALSE];
        [_nuevaCuentaIngresosButton setEnabled:FALSE];
        [_nuevaCuentaEgresosButton  setEnabled:FALSE];
    }
}

но это срабатывает только тогда, когда мое текстовое поле начинает редактировать, как это говорит, я хочу отключить кнопки, когда я получаю фокус на textField, а не когда я уже начал печатать


РЕДАКТИРОВАТЬ: собираюсь поставить мой код на основе помощи, полученной Джошуа Ноцци, он все еще не работает

MyNSTextField.h

#import <Cocoa/Cocoa.h>
@class MyNSTextField;

@protocol MyNSTextFieldDelegate

@optional -(BOOL)textFieldDidResignFirstResponder:(NSTextField *)sender;
@optional -(BOOL)textFieldDidBecomeFirstResponder:(NSTextField *)sender;

@end

@interface MyNSTextField : NSTextField

@property (strong, nonatomic)           id <MyNSTextFieldDelegate> cellView;

@end

MyNSTextField.m

#import "MyNSTextField.h"

@implementation MyNSTextField

- (BOOL)becomeFirstResponder
{
    BOOL status = [super becomeFirstResponder];
    if (status)

        [self.cellView textFieldDidBecomeFirstResponder:self];
    return status;
}

- (BOOL)resignFirstResponder
{
    BOOL status = [super resignFirstResponder];
    if (status)
        [self.cellView textFieldDidResignFirstResponder:self];
    return status;
}

@end

на моем viewcontroller EdicionDeCuentasWC.m

#import "MyNSTextField.h"


@interface EdicionDeCuentasWC ()<NSTableViewDataSource, NSTableViewDelegate, NSControlTextEditingDelegate, NSPopoverDelegate, MyNSTextFieldDelegate>
@end


@implementation EdicionDeCuentasWC
#pragma mark MyNSTextFieldDelegate
-(BOOL)textFieldDidBecomeFirstResponder:(NSTextField *)sender{
    NSLog(@"textFieldDidBecomeFirstResponder");
    return TRUE;
}

-(BOOL)textFieldDidResignFirstResponder:(NSTextField *)sender{
    NSLog(@"textFieldDidResignFirstResponder");
    return TRUE;
}
#pragma mark --
@end

важно сказать, что в визуальном редакторе все мои NSTextFields уже изменены на класс MyNSTextField и назначены делегаты для владельца моего файла (EdicionDeCuentasWC)

4 ответа

Я думаю, что прибил это. Я пытался создать подкласс NSTextFiled переопределить becomeFirstResponder() а также resignFirstResponder(), но как только я нажму на него, becomeFirstResponder() вызывается и resignFirstResponder() сразу после этого А? Но поле поиска выглядит так, как будто оно все еще находится в процессе редактирования, и все еще на нем.

Я понял, что когда вы нажимаете на поле поиска, поле поиска становится первым респондентом, но NSText будет подготовлено где-то позже, и фокус будет перенесен на NSText,

Я узнал, что когда NSText подготовлено, установлено self.currentEditor(), Проблема в том, что когда becomeFirstResponder()зовем, self.currentEditor() еще не установил. Так becomeFirstResponder() это не метод, чтобы обнаружить его фокус.

С другой стороны, когда фокус перемещается на NSTextтекстовое поле resignFirstResponder() называется, а ты знаешь что? self.currentEditor() установил. Итак, сейчас настало время сообщить своему делегату, что это текстовое поле сфокусировано.

Затем, как определить, когда поле поиска потеряло фокус. Опять же, это о NSText, Тогда вам нужно слушать NSText методы делегата, такие как textDidEndEditing()и убедитесь, что вы позволили суперклассу обработать метод и посмотреть, self.currentEditor() аннулирован Если это так, NSText потерял фокус и расскажет об этом делегату текстового поля.

Я даю код, на самом деле NSSearchField подкласс, чтобы сделать то же самое. И тот же принцип должен работать для NSTextField также.

protocol ZSearchFieldDelegate: NSTextFieldDelegate {
    func searchFieldDidBecomeFirstResponder(textField: ZSearchField)
    func searchFieldDidResignFirstResponder(textField: ZSearchField)
}


class ZSearchField: NSSearchField, NSTextDelegate {

    var expectingCurrentEditor: Bool = false

    // When you clicked on serach field, it will get becomeFirstResponder(),
    // and preparing NSText and focus will be taken by the NSText.
    // Problem is that self.currentEditor() hasn't been ready yet here.
    // So we have to wait resignFirstResponder() to get call and make sure
    // self.currentEditor() is ready.

    override func becomeFirstResponder() -> Bool {
        let status = super.becomeFirstResponder()
        if let _ = self.delegate as? ZSearchFieldDelegate where status == true {
            expectingCurrentEditor = true
        }
        return status
    }

    // It is pretty strange to detect search field get focused in resignFirstResponder()
    // method.  But otherwise, it is hard to tell if self.currentEditor() is available.
    // Once self.currentEditor() is there, that means the focus is moved from 
    // serach feild to NSText. So, tell it's delegate that the search field got focused.

    override func resignFirstResponder() -> Bool {
        let status = super.resignFirstResponder()
        if let delegate = self.delegate as? ZSearchFieldDelegate where status == true {
            if let _ = self.currentEditor() where expectingCurrentEditor {
                delegate.searchFieldDidBecomeFirstResponder(self)
                // currentEditor.delegate = self
            }
        }
        self.expectingCurrentEditor = false
        return status
    }

    // This method detect whether NSText lost it's focus or not.  Make sure
    // self.currentEditor() is nil, then that means the search field lost its focus,
    // and tell it's delegate that the search field lost its focus.

    override func textDidEndEditing(notification: NSNotification) {
        super.textDidEndEditing(notification)

        if let delegate = self.delegate as? ZSearchFieldDelegate {
            if self.currentEditor() == nil {
                delegate.searchFieldDidResignFirstResponder(self)
            }
        }
    }

}

Вам нужно будет изменить NSSerachField в ZSearchFieldи ваш класс клиента должен соответствовать ZSearchFieldDelegate не NSTextFieldDelegate, Вот пример. Когда пользователь нажимает на поле поиска, оно расширяет его ширину, а когда вы нажимаете на другое место, поле поиска теряет фокус и уменьшает ширину, изменяя значение NSLayoutConstraint устанавливается Интерфейсным Разработчиком.

class MyViewController: NSViewController, ZSearchFieldDelegate {

    // [snip]

    @IBOutlet weak var searchFieldWidthConstraint: NSLayoutConstraint!

    func searchFieldDidBecomeFirstResponder(textField: ZSearchField) {
        self.searchFieldWidthConstraint.constant = 300
        self.view.layoutSubtreeIfNeeded()
    }

    func searchFieldDidResignFirstResponder(textField: ZSearchField) {
        self.searchFieldWidthConstraint.constant = 100
        self.view.layoutSubtreeIfNeeded()
    }

}

Это может зависеть от поведения ОС, я пробовал на El Capitan 10.11.4, и это сработало.

Код также можно скопировать из Gist. https://gist.github.com/codelynx/aa7a41f5fd8069a3cfa2

У меня есть обычай NSTextField подкласс, который переопределяет -becomeFirstResponder а также -resignFirstResponder, это -cellView свойство требует соответствия протоколу, который объявляет -textDidBecome/ResignFirstResponder:(NSTextField *)sender но этого достаточно, чтобы дать вам общее представление. Его можно легко изменить, чтобы публиковать уведомления, для которых ваш контроллер может зарегистрироваться в качестве наблюдателя. Надеюсь, это поможет.

- (BOOL)becomeFirstResponder
{
    BOOL status = [super becomeFirstResponder];
    if (status)
        [self.cellView textFieldDidBecomeFirstResponder:self];
    return status;
}

- (BOOL)resignFirstResponder
{
    BOOL status = [super resignFirstResponder];
    if (status)
        [self.cellView textFieldDidResignFirstResponder:self];
    return status;
}

Я нашел следующий код на форумах macrumors.

  1. Является ли первый респондент текстовым представлением (редактор полей является текстовым представлением).
  2. Существует ли редактор полей?
  3. Является ли текстовое поле делегатом редактора полей?

Вроде работает.

- (BOOL)isTextFieldInFocus:(NSTextField *)textField
{
    BOOL inFocus = NO;

    inFocus = ([[[textField window] firstResponder] isKindOfClass:[NSTextView class]]
               && [[textField window] fieldEditor:NO forObject:nil]!=nil
               && [textField isEqualTo:(id)[(NSTextView *)[[textField window] firstResponder]delegate]]);

    return inFocus;
}

На всякий случай, как небольшую вариацию идеи @sam, мы можем наблюдать NSWindow.firstResponderСамо свойство, по документации KVO-совместимо. Затем сравните это сtextField или textField.currentEditor() чтобы выяснить, сфокусировано ли поле.

Swift3.0

Проверьте, выделен ли текст в NSTextField

if theSearchTextField.currentEditor()?.selectedRange.length == 0{
    print("IS SeletedAll")
}else{
    print("NOT SeletedAll")
}
Другие вопросы по тегам