привязка NSTextField к NSNumber

Я пытаюсь использовать NSTextField для ввода целых чисел. Текстовое поле привязано к свойству NSNumber, и в методе установки я очищаю входное значение (убедитесь, что это int) и при необходимости устанавливаю свойство. Я отправляю willChangeValueForKey: и didChangeValueForKey :, но пользовательский интерфейс не обновляется до новых значений, пока это текстовое поле все еще активно.

Так, например, я могу ввести «12abc» в текстовое поле, которое метод установки очищает до «12», но в текстовом поле по-прежнему отображается «12abc».

У меня установлен флажок «Постоянно обновлять значение» в построителе интерфейса.

(Я также заметил, что метод установки получает NSString, а не NSNumber. Это нормально?)

Как правильно подключить NSTextField к NSNumber? Как выглядит метод установки для свойства? Как предотвратить отображение нечисловых значений в текстовом поле?


person lajos    schedule 09.02.2009    source источник


Ответы (4)


Я отправляю willChangeValueForKey: и didChangeValueForKey :, но пользовательский интерфейс не обновляется до новых значений, пока это текстовое поле все еще активно.

Причин для отправки таких сообщений очень мало. Обычно вы можете выполнять ту же работу лучше и чище, реализуя и используя аксессоры (или, еще лучше, свойства). KVO отправит вам уведомления, когда вы это сделаете.

В вашем случае вы хотите либо отклонить, либо отфильтровать ложные входные данные (например, «12abc»). Правильный инструмент для этой задачи - проверка значения ключа.

Чтобы включить это, установите флажок «Проверяет немедленно» в привязке в IB и реализуйте метод проверки.

Фильтрация:

- (BOOL) validateMyValue:(inout NSString **)newValue error:(out NSError **)outError {
    NSString *salvagedNumericPart;
    //Determine whether you can salvage a numeric part from the string; in your example, that would be “12”, chopping off the “abc”.
    *newValue = salvagedNumericPart; //@"12"
    return (salvagedNumericPart != nil);
}

Отклонение:

- (BOOL) validateMyValue:(inout NSString **)newValue error:(out NSError **)outError {
    BOOL isEntirelyNumeric;
    //Determine whether the whole string (perhaps after stripping whitespace) is a number. If not, reject it outright.
    if (isEntirelyNumeric) {
        //The input was @"12", or it was @" 12 " or something and you stripped the whitespace from it, so *newValue is @"12".
        return YES;
    } else {
        if (outError) {
            *outError = [NSError errorWithDomain:NSCocoaErrorDomain code: NSKeyValueValidationError userInfo:nil];
        }
        //Note: No need to set *newValue here.
        return NO;
    }
}

(Я также заметил, что метод установки получает NSString, а не NSNumber. Это нормально?)

Да, если вы не используете преобразователь значений, который преобразует строки в числа, не подключаете средство форматирования чисел к выходу formatter или не заменяете NSNumber на NSString в вашем методе проверки.

person Peter Hosey    schedule 09.02.2009
comment
Методы проверки, похоже, имеют сигнатуру: - (BOOL) validateNewValue: (id *) ioValue error: (NSError **) outError; Но даже если я реализую метод с правильной подписью и верну НЕТ или исправлю значение, в текстовом поле все равно будут отображаться нечисловые символы. - person lajos; 09.02.2009
comment
Спасибо за замечание о сигнатуре метода - я исправил свой ответ. Вы отметили поле «Проверяет немедленно»? Я не уверен, в чем еще может быть проблема, не видя кода. - person Peter Hosey; 10.02.2009
comment
Я разместил здесь тестовый проект: www.codza.com/files/numericFieldTest.zip. Метод проверки правильно возвращает ДА ​​или НЕТ (см. Журналы), но в текстовом поле по-прежнему отображаются буквенные символы. Большое спасибо, что посмотрели на это. Вы можете связаться со мной по адресу www.codza.com/contact, и я также проверю комментарии здесь. - person lajos; 10.02.2009
comment
Переменная вашего экземпляра не обязательно должна быть IBOutlet. В остальном приложение работает должным образом в отношении того, что вы делаете, а именно отказа. Если вам нужна фильтрация, вам нужно просканировать число из строки, и если это удастся, замените строку числом и верните YES. - person Peter Hosey; 10.02.2009
comment
Спасибо, что посмотрели на это. Я пробовал заменить возврат NO; с * ioValue = (id) [intValue stringValue]; return YES ;, но альфа-символы по-прежнему отображаются в текстовом поле, пока пользователь печатает. Я понимаю, что этот метод проверяет значение за кулисами, но я хотел бы предотвратить ввод - person lajos; 10.02.2009
comment
символов, пока активно текстовое поле. Может просто не так все устроено? - person lajos; 10.02.2009
comment
Ах я вижу. Для такой проверки в реальном времени, я думаю, вам нужно быть делегатом поля и реализовать controlTextDidChange :, который является одним из методов делегата NSControl. Если вы определили, что новый текст недействителен, подайте звуковой сигнал и измените текст элемента управления (или редактора полей?) Обратно на старый текст. - person Peter Hosey; 10.02.2009
comment
Кроме того, не забывайте, что вы можете вызвать метод проверки объекта модели из метода controlTextDidChange: вашего контроллера и получить лучшее из обоих миров. (Модель и контроллер обычно являются отдельными объектами в реальном приложении.) - person Peter Hosey; 10.02.2009

К отличному ответу Питера Хози есть важный комментарий, который я хотел бы вывести на высший уровень (потому что я пропустил его в первый раз).

Если вы хотите проверять / изменять NSTextField каждый раз, когда вводится символ, а не только тогда, когда пользователь отправляет поле, тогда вы не сможете получить то, что хотите, только от привязок. Вам нужно назначить делегата в текстовое поле, а затем реализовать - (void)controlTextDidChange:(NSNotification *)aNotification в делегате. Это будет вызываться каждый раз при изменении текста. Если хотите, вы можете вызвать валидатор значений в controlTextDidChange.

Например:

- (void)controlTextDidChange:(NSNotification *)aNotification
{
    NSError *outError;
    NSControl *textField = [aNotification object];
    NSString *myText = [textField stringValue];

    // myObject is the model object that owns the text in question
    // the validator can modify myText by reference
    [myObject validateValue:&myText error:&outError]];

    // update the NSNextField with the validated text
    [postingObject setStringValue:myText];
}
person Jon Shea    schedule 07.05.2009

Как упоминал Бен, один из способов сделать это - прикрепить NSNumberFormatter к вашему текстовому полю, что довольно просто настроить в построителе интерфейса и, вероятно, будет Just Work ™.

Если вам не нравятся модальные диалоги, которые NSNumberFormatter бросает вашим пользователям, когда они вводят нечисловые значения, вы можете создать подкласс NSNumberFormatter для реализации другого, «более снисходительного» поведения форматирования. Я думаю, что переопределение - numberFromString: удаление нечисловых символов перед вызовом реализации super должно помочь.

Другой подход - создать и зарегистрировать собственный подкласс NSValueTransformer для синтаксического анализа строк в NSNumbers и обратно, но я бы сначала попробовал использовать NSNumberFormatter, поскольку это довольно четко класс, который был разработан именно для этой цели.

Подробнее о преобразователях стоимости

person Dirk Stoop    schedule 09.02.2009

Думаю, вам нужно настроить форматировщик номеров на свой NSTextField.

Прочтите о средствах форматирования на веб-сайте Apple: Применение средств форматирования.

person Sophie Alpert    schedule 09.02.2009
comment
Я добавил средство форматирования чисел, поместив его в текстовое поле, как предлагается в указанном вами документе, но ничего не происходит. Я все еще могу вводить нечисловые символы. Нужно ли мне что-то делать, кроме подключения форматировщика к текстовому полю? - person lajos; 09.02.2009