Каков наилучший способ проверки ввода валюты в UITextField? - PullRequest
17 голосов
/ 05 декабря 2008

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

enter image description here

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

enter image description here

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

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

Ответы [ 13 ]

27 голосов
/ 31 января 2011

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

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

- (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);
}
21 голосов
/ 19 июня 2009

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

Это хорошо работает для меня:


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

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

}

10 голосов
/ 06 сентября 2012

Я нашел это относительно чистым подходом. Я не тестировал его с неамериканскими валютами, но поскольку он использует свойства 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;
}
7 голосов
/ 05 декабря 2008

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

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

7 голосов
/ 05 декабря 2008

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

6 голосов
/ 10 февраля 2011

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

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

5 голосов
/ 12 мая 2011

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

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

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

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

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

2 голосов
/ 22 октября 2011

Найдя быстрое решение по переполнению стека для обработки валюты США, я переписал функцию для безопасной работы с международными валютами. Это решение будет динамически проверять пользовательский ввод из 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.
}
2 голосов
/ 19 июня 2009

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

1 голос
/ 17 января 2012

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

- (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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...