Как лучше всего проверить ввод валюты в UITextField?

Мое приложение позволяет пользователю вводить числовое значение (валюту) в элементе управления UITextField, но желаемая раскладка клавиатуры, к сожалению, не является одной из встроенных опций, поэтому мне пришлось выбрать опцию «Числа и пунктуация» в Интерфейсном Разработчике. Вот соответствующее диалоговое окно в IB:

Как лучше всего проверить ввод валюты в UITextField?

Поэтому, когда мое приложение запрашивает у пользователя ввод, оно отображает следующее:

Как лучше всего проверить ввод валюты в UITextField?

Что совершенно нормально, но посмотрите на все дополнительные ключи, доступные пользователю! Можно было легко ввести "12; 56!" в текстовом поле, и я предполагаю, что мне нужно как-то это проверить.

Итак, мой вопрос: как мне проверить значения валюты, введенные в UITextField?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
18
0
20 875
13
Перейти к ответу Данный вопрос помечен как решенный

Ответы 13

Ответ принят как подходящий

Возможно, вы могли бы прикрепить UITextFieldDelegate к элементу управления и заставить его реализовать textField:shouldChangeCharactersInRange:replacementString:. Таким образом, если он увидит какие-либо символы, которые вам не нужны в поле, он сможет их отклонить.

В сочетании с предложением textField:shouldChangeCharactersInRange:replacementString:, сделанным Марком, вы должны передать текст через NSNumberFormatter, используя NSNumberFormatterCurrencyStyle. Это позволит справиться с особенностями форматирования валюты и обработать параметры, специфичные для локали.

Если вы его найдете, в документации iPhone есть раздел «Руководство по программированию форматирования данных для какао». К сожалению, большая часть информации о пользовательском интерфейсе здесь предназначена для Mac OS X (не работает на iPhone), но она покажет вам, как использовать классы форматирования.

Использование NSNumberFormatter - правильный ответ. Он будет обрабатывать проверку и преобразовывать строку в правильный тип объекта и обратно.

Хотя вы можете возиться с NSNumberFormatter, мне было проще просто отсеивать все, кроме 0-9 и.

У меня это хорошо работает:


- (BOOL) textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
    NSCharacterSet *nonNumberSet = [[NSCharacterSet characterSetWithCharactersInString:@"0123456789."] invertedSet];

return ([string stringByTrimmingCharactersInSet:nonNumberSet].length > 0);

}

как насчет отрицательных чисел? разве вы не хотите добавить «-» к действительному набору, который вы инвертируете?

Erich Mirabal 19.06.2009 18:27

как насчет того, если пользователь хочет удалить введенные цифры?

ashish 01.12.2010 15:39

Приведенный выше код работает, за исключением того, что клавиша Backspace также отключена (как упоминалось в ashish)

topace 12.10.2011 13:54

Если вы хотите, чтобы они могли вводить только цифры - вы можете также рассмотреть цифровую клавиатуру.

Не поможет, если пользователь подключил клавиатуру bluetooth, так как тогда он может вводить то, что ему нравится.

Thomas Tempelmann 12.05.2011 00:36

Использование shouldChangeCharactersInRange портит всплывающую клавиатуру, поскольку кнопка возврата не работает.

Средство форматирования чисел - это то, что вам нужно. Вот образец, который я использовал, чтобы найти положительные десятичные дроби. Я называю это во время проверки - например, когда пользователь нажимает кнопку сохранения.

    -(BOOL) isPositiveNumber: (NSString *) numberString {

    NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
    [numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];

    NSNumber *aNumber  =  [numberFormatter numberFromString:numberString];
    [numberFormatter release];

    if ([aNumber floatValue] > 0) {
        NSLog( @"Found positive number %4.2f",[aNumber floatValue] );

        return YES;
    }
    else {
        return NO;
    }
}

Вы можете обнаружить обратное пространство, так как диапазон отрицательный.

drewish 23.07.2012 18:41

Проблема с этим кодом в том, что вы не можете удалить значение. Введите 1 и попробуйте вернуть его на место. Пустая строка приведет к ошибке numberFromString, сделав aNumber nil, а [nil floatValue] даст 0, что означает, что изменение не примет.

drewish 23.07.2012 19:10

Вы также можете использовать решение Gamma-Point и оценить, равно ли * aNumber Nil, если его Nil, то это означает символ, который не был 0-9. или, было введено, таким образом вы можете проверять только числа, он обнулит переменную, если введен какой-либо символ без числа.

У меня есть желание ответить, потому что это была первая запись, которую я увидел, когда я погуглил, и ответ с самым высоким рейтингом не позволил мне ввести валюту. Я немец, и в Германии (и во многих других странах) мы используем , в качестве десятичного разделителя.

Я только что написал похожий метод, и это то, что у меня есть прямо сейчас.

- (BOOL) textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    static NSString *numbers = @"0123456789";
    static NSString *numbersPeriod = @"01234567890.";
    static NSString *numbersComma = @"0123456789,";

    //NSLog(@"%d %d %@", range.location, range.length, string);
    if (range.length > 0 && [string length] == 0) {
        // enable delete
        return YES;
    }

    NSString *symbol = [[NSLocale currentLocale] objectForKey:NSLocaleDecimalSeparator];
    if (range.location == 0 && [string isEqualToString:symbol]) {
        // decimalseparator should not be first
        return NO;
    }
    NSCharacterSet *characterSet;
    NSRange separatorRange = [textField.text rangeOfString:symbol];
    if (separatorRange.location == NSNotFound) {
        if ([symbol isEqualToString:@"."]) {
            characterSet = [[NSCharacterSet characterSetWithCharactersInString:numbersPeriod] invertedSet];
        }
        else {
            characterSet = [[NSCharacterSet characterSetWithCharactersInString:numbersComma] invertedSet];              
        }
    }
    else {
        // allow 2 characters after the decimal separator
        if (range.location > (separatorRange.location + 2)) {
            return NO;
        }
        characterSet = [[NSCharacterSet characterSetWithCharactersInString:numbers] invertedSet];               
    }
    return ([[string stringByTrimmingCharactersInSet:characterSet] length] > 0);
}

Это здорово, спасибо. Только что у клиента был плач из-за более чем двух знаков после запятой :)

tomwilson 16.08.2011 11:21

Это, безусловно, более комплексное решение. Я предлагаю сделать это принятым ответом.

Ceryni 20.06.2015 04:37

Я обнаружил, что shouldChangeCharactersInRange также закручивает всплывающую клавиатуру, возврат и кнопку «Готово». Я обнаружил, что если я обрабатываю строки нулевой длины и разрешаю управляющие символы, все работает нормально.

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

Вот код, который я использовал:

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
    if ([string length] < 1)    // non-visible characters are okay
        return YES;
    if ([string stringByTrimmingCharactersInSet:[NSCharacterSet controlCharacterSet]].length == 0)
        return YES;
    return ([string stringByTrimmingCharactersInSet:[self.characterSet invertedSet]].length > 0);
}

Где self.characterSet содержит допустимые символы, я использовал этот метод для создания его для валюты:

- (NSCharacterSet *)createCurrencyCharacterSet
{
    NSLocale *locale = [NSLocale currentLocale];
    NSMutableCharacterSet *currencySet = [NSMutableCharacterSet decimalDigitCharacterSet];

    [currencySet addCharactersInString:@"-"];       // negative symbol, can't find a localised version
    [currencySet addCharactersInString:[locale objectForKey:NSLocaleCurrencySymbol]];
    [currencySet addCharactersInString:[locale objectForKey:NSLocaleGroupingSeparator]];
    [currencySet addCharactersInString:[locale objectForKey:NSLocaleDecimalSeparator]];

    return [[currencySet copy] autorelease];
}

Несколько неудачный код [[currencySet copy] autorelease] возвращает неизменный NSCharacterSet.

Использование [NSCharacterSet decimalDigitCharacterSet] также включает в себя индийские и арабские эквивалентные символы, что, как мы надеемся, означает, что люди, использующие эти языки, могут использовать цифры своего алфавита для ввода чисел.

По-прежнему необходимо проверить, может ли NSNumberFormatter анализировать ввод пользователя и предупреждать, если он не может; тем не менее, это дает больше удовольствия, когда можно вводить только допустимые символы.

Я все время вижу, что люди не знают, как правильно определить результирующую строку при реализации shouldChangeCharactersInRange.

Вот как вы получите новый текст, который будет введен, если вы ответите ДА:

NSString *newText = [textField.text stringByReplacingCharactersInRange:range withString:string];

Когда у вас есть этот новый текст, легко проверить, является ли этот текст допустимым числом или нет, и вернуть ДА или НЕТ соответственно.

Имейте в виду, что все другие решения, такие как показанные здесь, где проверяется длина «строки» и т. д., Могут работать некорректно, если пользователь пытается отредактировать строку с помощью клавиатуры Bluetooth с клавишами управления курсором или с помощью дополнительных функции редактирования (выбрать, вырезать, вставить).

Я искал в Google только эту проблему, и каким-то образом Google ее нашел. Престижность.

David M. 13.04.2012 07:29

Найдя быстрое решение проблемы переполнения стека для работы с валютой США, я переписал функцию для безопасной работы с международными валютами. Это решение будет динамически проверять ввод данных пользователем из UITextField, корректируя их по мере ввода.

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {      

   NSNumberFormatter *nf = [[NSNumberFormatter alloc] init];

   [numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
   [numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
   [numberFormatter setMaximumFractionDigits:2];
   [numberFormatter setMinimumFractionDigits:0];

    // Grab the contents of the text field
    NSString *text = [textField text];  

    // the appropriate decimalSeperator and currencySymbol for the current locale
    // can be found with help of the
    // NSNumberFormatter and NSLocale classes.
    NSString *decimalSeperator = numberFormatter.decimalSeparator;  
    NSString *currencySymbol = numberFormatter.currencySymbol;

    NSString *replacementText = [text stringByReplacingCharactersInRange:range withString:string];
    NSMutableString *newReplacement = [[ NSMutableString alloc ] initWithString:replacementText];

    // whenever a decimalSeperator or currencySymobol is entered, we'll just update the textField.
    // whenever other chars are entered, we'll calculate the new number and update the textField accordingly.
    // If the number can't be computed, we ignore the new input.
    NSRange decimalRange = [text rangeOfString:decimalSeperator];
    if ([string isEqualToString:decimalSeperator] == YES && 
        [text rangeOfString:decimalSeperator].length == 0) {
        [textField setText:newReplacement];
    } else if ([string isEqualToString:currencySymbol] == YES&& 
              [text rangeOfString:currencySymbol].length == 0) {
        [textField setText:newReplacement];        
    } else if ([newReplacement isEqualToString:currencySymbol] == YES) {
        return YES;
    }else {

        NSString *currencyGroupingSeparator = numberFormatter.currencyGroupingSeparator;

        [newReplacement replaceOccurrencesOfString:currencyGroupingSeparator withString:@"" options:NSBackwardsSearch range:NSMakeRange(0, [newReplacement length])];        
        [numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];

        NSNumber *number = [numberFormatter numberFromString:newReplacement];

        if ([newReplacement length] == 1) {
            [numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
            number = [numberFormatter numberFromString:newReplacement];
        }

        if (number == nil) { 
            [newReplacement release];
            return NO;
        }
        [numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];    
        text = [numberFormatter stringFromNumber:number];
        [textField setText:text];       
    }

    [newReplacement release];

    return NO; // we return NO because we have manually edited the textField contents.
}

спасибо Брайан, вы заметили, что в вашем коде вы не можете ввести 0 после десятичной дроби

zambono 26.12.2011 23:35

Не удалось найти подходящую реализацию для проверки валюты. Вот мой вариант:

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {

   NSString* proposedString = [textField.text stringByReplacingCharactersInRange:range withString:string];

   //if there is empty string return YES
   if ([proposedString length] == 0) {
       return YES;
   }

   //create inverted set for appripriate symbols
   NSCharacterSet *nonNumberSet = [[NSCharacterSet characterSetWithCharactersInString:@"0123456789.,"] invertedSet];

   //if side symbols is trimmed by nonNumberSet - return NO
   if ([proposedString stringByTrimmingCharactersInSet:nonNumberSet].length != [proposedString length]) {
       return NO;
   }

   //if there is more than 1 symbol of '.' or ',' return NO
   if ([[proposedString componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@".,"]] count] > 2) {
      return NO;
   }

   //finally check is ok, return YES
   return YES;
}

Я обнаружил, что это относительно чистый подход. Я не тестировал его с валютами, отличными от США, но, поскольку он использует свойства NSNumberFormatter, я считаю, что он должен обрабатывать их правильно.

Начните с настройки форматтера в каком-нибудь месте:

    formatter = [NSNumberFormatter new];
    [formatter setNumberStyle: NSNumberFormatterCurrencyStyle];
    [formatter setLenient:YES];
    [formatter setGeneratesDecimalNumbers:YES];

Используйте средство форматирования для анализа и переформатирования их ввода. Он также обрабатывает сдвиг числа при добавлении и удалении цифр.

-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
    NSString *replaced = [textField.text stringByReplacingCharactersInRange:range withString:string];
    NSDecimalNumber *amount = (NSDecimalNumber*) [formatter numberFromString:replaced];
    if (amount == nil) {
        // Something screwed up the parsing. Probably an alpha character.
        return NO;
    }
    // If the field is empty (the initial case) the number should be shifted to
    // start in the right most decimal place.
    short powerOf10 = 0;
    if ([textField.text isEqualToString:@""]) {
        powerOf10 = -formatter.maximumFractionDigits;
    }
    // If the edit point is to the right of the decimal point we need to do
    // some shifting.
    else if (range.location + formatter.maximumFractionDigits >= textField.text.length) {
        // If there's a range of text selected, it'll delete part of the number
        // so shift it back to the right.
        if (range.length) {
            powerOf10 = -range.length;
        }
        // Otherwise they're adding this many characters so shift left.
        else {
            powerOf10 = [string length];
        }
    }
    amount = [amount decimalNumberByMultiplyingByPowerOf10:powerOf10];

    // Replace the value and then cancel this change.
    textField.text = [formatter stringFromNumber:amount];
    return NO;
}

Это действительно рабочий подход. В большинстве случаев работает как амулет.

Eugene Prokoshev 22.05.2014 20:37

Что происходит, когда символ валюты находится сзади?

super9 11.06.2014 12:48

Другие вопросы по теме