Убедитесь, что ввод в UITextField только числовой


Ответы:


35

Я использую этот код в своем приложении для Mac, такой же или аналогичный должен работать с iPhone. Он основан на регулярных выражениях RegexKitLite и окрашивает текст в красный цвет, когда он недействителен.

static bool TextIsValidValue( NSString* newText, double &value )
{
    bool result = false;

    if ( [newText isMatchedByRegex:@"^(?:|0|[1-9]\\d*)(?:\\.\\d*)?$"] ) {
        result = true;
        value = [newText doubleValue];
    }
    return result;
}

- (IBAction) doTextChanged:(id)sender;
{
    double value;
    if ( TextIsValidValue( [i_pause stringValue], value ) ) {
        [i_pause setTextColor:[NSColor blackColor]];
        // do something with the value
    } else {
        [i_pause setTextColor:[NSColor redColor]];
    }
}

7
Да, это ужасно старый ответ, я знаю, но я думал, что статические методы были объявлены так: + (BOOL) TextIsValidValue:(NSString*)newText:(double)&value:...
RCIX

7
TextIsValidValue - это статическая функция C, что означает, что она является локальной для файла и не является частью какого-либо объекта. Вы (@RCIX) описываете метод объекта, который похож на статический метод в C ++ и представляет собой совершенно другую концепцию по сравнению со статической функцией C (или статической переменной, если на то пошло!).
Питер Н Льюис

Выражение соответствует пустой строке (не числовой).
Николай Рухе

Как правило, для сопоставления числа для ввода необходимо разрешить пустую строку, иначе пользователь не может, например, ввести «1 <delete> 2».
Питер Н Льюис

6
Для тех разработчиков, которые не знают, что такое регулярные выражения и как они работают, просто перейдите к этому wesbite декодера регулярных выражений и скопируйте / вставьте, (?:|0|[1-9]\\d*)(?:\\.\\d*)чтобы увидеть, что это означает. Также см. Regex Wikipedia
Дорогая,

117

Вы можете сделать это в нескольких строках, например:

BOOL valid;
NSCharacterSet *alphaNums = [NSCharacterSet decimalDigitCharacterSet];
NSCharacterSet *inStringSet = [NSCharacterSet characterSetWithCharactersInString:myInputField.text];
valid = [alphaNums isSupersetOfSet:inStringSet];    
if (!valid) // Not numeric

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


22
в этом ответе не проверяются
двоеточие

72

Есть несколько способов сделать это:

  1. Используйте метод NSNumberFormatter numberFromString :. Это вернет NSNumber, если он может правильно проанализировать строку или nilне может.
  2. Используйте NSScanner
  3. Удалите любой нечисловой символ и посмотрите, соответствует ли строка
  4. Используйте регулярное выражение

IMO, используя что - то вроде -[NSString doubleValue]бы не самый лучший вариант , потому что как @"0.0"и @"abc"будет иметь doubleValue от 0. * Методы значение , все возвращаются 0 , если они не в состоянии преобразовать строку правильно, поэтому было бы трудно отличить допустимая строка @"0"и недопустимая строка. Что-то вроде strtolфункции C будет иметь ту же проблему.

Я думаю, что использование NSNumberFormatter было бы лучшим вариантом, поскольку он принимает во внимание локаль (т. Е. Число @"1,23"в Европе, а не @"1.23"в США).


Хммм, должно быть, я неправильно установил параметры для NSNumberFormatter. Когда я использую numberFromString со строкой «34jfkjdskj80», она возвращает число 3480. Кажется, что вместо возврата nil просто удаляются символы.
Тони Эйхельбергер,

5
@Tony: да, я не уверен, что вы делаете, потому что следующие журналы "N: (null)" для меня:NSNumberFormatter * f = [[NSNumberFormatter alloc] init]; NSNumber * n = [f numberFromString:@"34jfkjdskj80"]; NSLog(@"N: %@", n);
Дэйв Делонг,

2
Это немного устарело, но на случай, если кто-нибудь это прочитает, я подумал, что стоит отметить, что NSScannerвроде бы NSNumberFormatterучитывает языковой стандарт при синтаксическом анализе строки, при условии, что вы используете setLocale:объект сканера (вы могли бы, например, предоставить [NSLocale currentLocale]).
dreamlax

1
Некоторые читают «Войну и мир». Остальные предпочитают «Моби Дик». Лично я думаю, что просто собираюсь извлечь набор данных Stack Overflow, отфильтровать вопросы с ответами Дейва и просто наслаждаться.
БП.

1
@MarkAmery Да, на такое поведение можно положиться. Хотя это может и не быть задокументировано, именно так оно и вело себя в течение многих лет, и изменить его было бы невозможно с точки зрения двоичной совместимости.
Дэйв Делонг

19

Если вы хотите, чтобы пользователю было разрешено вводить только цифры, вы можете заставить ViewController реализовывать часть UITextFieldDelegate и определить этот метод:

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
  NSString *resultingString = [textField.text stringByReplacingCharactersInRange: range withString: string];

  // The user deleting all input is perfectly acceptable.
  if ([resultingString length] == 0) {
    return true;
  }

  NSInteger holder;

  NSScanner *scan = [NSScanner scannerWithString: resultingString];

  return [scan scanInteger: &holder] && [scan isAtEnd];
}

Возможно, есть более эффективные способы, но я считаю это довольно удобным . И метод должен быть легко адаптирован для проверки двойников или чего-то еще: просто используйте scanDouble: или аналогичный.


13
#pragma mark - UItextfield Delegate

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    if ([string isEqualToString:@"("]||[string isEqualToString:@")"]) {
        return TRUE;
    }

    NSLog(@"Range ==%d  ,%d",range.length,range.location);
    //NSRange *CURRANGE = [NSString  rangeOfString:string];

    if (range.location == 0 && range.length == 0) {
        if ([string isEqualToString:@"+"]) {
            return TRUE;
        }
    }
    return [self isNumeric:string];
}

-(BOOL)isNumeric:(NSString*)inputString{
    BOOL isValid = NO;
    NSCharacterSet *alphaNumbersSet = [NSCharacterSet decimalDigitCharacterSet];
    NSCharacterSet *stringSet = [NSCharacterSet characterSetWithCharactersInString:inputString];
    isValid = [alphaNumbersSet isSupersetOfSet:stringSet];
    return isValid;
}

11

Вот несколько однострочников, которые объединяют ответ Питера Льюиса выше ( убедитесь, что вход в UITextField является только числовым ) с NSPredicates

    #define REGEX_FOR_NUMBERS   @"^([+-]?)(?:|0|[1-9]\\d*)(?:\\.\\d*)?$"
    #define REGEX_FOR_INTEGERS  @"^([+-]?)(?:|0|[1-9]\\d*)?$"
    #define IS_A_NUMBER(string) [[NSPredicate predicateWithFormat:@"SELF MATCHES %@", REGEX_FOR_NUMBERS] evaluateWithObject:string]
    #define IS_AN_INTEGER(string) [[NSPredicate predicateWithFormat:@"SELF MATCHES %@", REGEX_FOR_INTEGERS] evaluateWithObject:string]

Пожалуйста, дайте мне ссылку, по которой я могу узнать об этом больше.
Смит Сарайя

4

Для целочисленного теста это будет:

- (BOOL) isIntegerNumber: (NSString*)input
{
    return [input integerValue] != 0 || [input isEqualToString:@"0"];
}

Хороший ответ, но все же проблема с этим подходом будет заключаться в том, чтобы входное значение 232asdfsadf считалось числом.
Whoami 08

вы можете сравнить длину integerValue (повторно преобразовать в строку) с исходной строкой, чтобы убедиться, что "123aoenuthr" не считается целым числом.
ThePrimeagen

3

Вы можете использовать doubleValue своей строки, например

NSString *string=@"1.22";
double a=[string doubleValue];

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


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

не говоря уже о проведении буквальных сравнений равенства чисел с плавающей запятой - это просто плохая практика
М. Райан

3

У меня была точно такая же проблема, и я не вижу ответ, который я опубликовал, так что вот он.

Я создал и подключил свое текстовое поле через IB. Когда я подключил его к своему коду с помощью Control + Drag, я выбрал Action, а затем выбрал событие Editing Changed. Это запускает метод для каждого ввода символа. Вы можете использовать другое мероприятие по своему усмотрению.

Впоследствии я использовал этот простой код для замены текста. Обратите внимание, что я создал свой собственный набор символов, включающий десятичный / периодический символ и числа. В основном разделяет строку на недопустимые символы, а затем объединяет их с пустой строкой.

- (IBAction)myTextFieldEditingChangedMethod:(UITextField *)sender {
        NSCharacterSet *validCharacterSet = [NSCharacterSet characterSetWithCharactersInString:@".0123456789"];
        NSCharacterSet *invalidCharacterSet = validCharacterSet.invertedSet;
        sender.text = [[sender.text componentsSeparatedByCharactersInSet:invalidCharacterSet] componentsJoinedByString:@""];
}

Кредиты: удалить все, кроме цифр, из NSString


Отрицательные числа (-)?
CMVR 02

3

Поздно в игре, но я использую удобную небольшую категорию, которая учитывает десятичные разряды и используемый для них локальный символ. ссылка на его суть здесь

@interface NSString (Extension)

- (BOOL) isAnEmail;
- (BOOL) isNumeric;

@end

@implementation NSString (Extension)

/**
 *  Determines if the current string is a valid email address.
 *
 *  @return BOOL - True if the string is a valid email address.
 */

- (BOOL) isAnEmail
{
    NSString *emailRegex = @"[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}";
    NSPredicate *emailTest = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", emailRegex];

    return [emailTest evaluateWithObject:self];
}

/**
 *  Determines if the current NSString is numeric or not. It also accounts for the localised (Germany for example use "," instead of ".") decimal point and includes these as a valid number.
 *
 *  @return BOOL - True if the string is numeric.
 */

- (BOOL) isNumeric
{
    NSString *localDecimalSymbol = [[NSLocale currentLocale] objectForKey:NSLocaleDecimalSeparator];
    NSMutableCharacterSet *decimalCharacterSet = [NSMutableCharacterSet characterSetWithCharactersInString:localDecimalSymbol];
    [decimalCharacterSet formUnionWithCharacterSet:[NSCharacterSet alphanumericCharacterSet]];

    NSCharacterSet* nonNumbers = [decimalCharacterSet invertedSet];
    NSRange r = [self rangeOfCharacterFromSet: nonNumbers];

    if (r.location == NSNotFound)
    {
        // check to see how many times the decimal symbol appears in the string. It should only appear once for the number to be numeric.
        int numberOfOccurances = [[self componentsSeparatedByString:localDecimalSymbol] count]-1;
        return (numberOfOccurances > 1) ? NO : YES;
    }
    else return NO;
}

@end

не должен ли alphanumericCharacterSet быть decimalDigitCharacterSet в isNumeric? вот как я его использую.
Андрей Радулеску

Не работает, @ "A" получить результат isNumeric YES, но это не число
Gank

3
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
    if(string.length > 0)
    {
        NSCharacterSet *numbersOnly = [NSCharacterSet characterSetWithCharactersInString:@"0123456789"];
        NSCharacterSet *characterSetFromTextField = [NSCharacterSet characterSetWithCharactersInString:string];

        BOOL stringIsValid = [numbersOnly isSupersetOfSet:characterSetFromTextField];
        return stringIsValid;
    }
    return YES;
}

3

ИМО лучший способ достичь вашей цели - отобразить цифровую клавиатуру, а не обычную клавиатуру. Это ограничивает, какие ключи доступны пользователю. Это устраняет необходимость в проверке и, что еще более важно, предотвращает ошибку пользователя. Цифровая клавиатура также намного удобнее для ввода цифр, потому что клавиши значительно больше.

В построителе интерфейса выберите UITextField, перейдите в Инспектор атрибутов и измените «Тип клавиатуры» на «Десятичный блокнот».

введите описание изображения здесь

В результате клавиатура будет выглядеть так:

введите описание изображения здесь

Осталось только убедиться, что пользователь не вводит два десятичных знака. Вы можете сделать это, пока они редактируют. Добавьте следующий код в свой контроллер представления. Этот код удаляет второй десятичный разряд сразу после его ввода. Пользователю кажется, что второй знак после запятой никогда не появлялся.

- (void)viewDidLoad
{
  [super viewDidLoad];

  [self.textField addTarget:self
                    action:@selector(textFieldDidChange:)
           forControlEvents:UIControlEventEditingChanged];
}

- (void)textFieldDidChange:(UITextField *)textField
{
  NSString *text = textField.text;
  NSRange range = [text rangeOfString:@"."];

  if (range.location != NSNotFound &&
      [text hasSuffix:@"."] &&
      range.location != (text.length - 1))
  {
    // There's more than one decimal
    textField.text = [text substringToIndex:text.length - 1];
  }
}

1
не забывайте о возможности копирования и вставки - ваш подход в этом случае не работает
slxl

1
Это касается не всех случаев. Пользователь мог скопировать текст или использовать клавиатуру Bluetooth.
bean

2
@property (strong) NSNumberFormatter *numberFormatter;
@property (strong) NSString *oldStringValue;

- (void)awakeFromNib 
{
  [super awakeFromNib];
  self.numberFormatter = [[NSNumberFormatter alloc] init];
  self.oldStringValue = self.stringValue;
  [self setDelegate:self];
}

- (void)controlTextDidChange:(NSNotification *)obj
{
  NSNumber *number = [self.numberFormatter numberFromString:self.stringValue];
  if (number) {
    self.oldStringValue = self.stringValue;
  } else {
    self.stringValue = self.oldStringValue;
  }
}

2

Старая ветка, но стоит упомянуть, что Apple представила NSRegularExpressioniOS 4.0. (Взяв регулярное выражение из ответа Питера)

// Look for 0-n digits from start to finish
NSRegularExpression *noFunnyStuff = [NSRegularExpression regularExpressionWithPattern:@"^(?:|0|[1-9]\\d*)(?:\\.\\d*)?$" options:0 error:nil];

// There should be just one match
if ([noFunnyStuff numberOfMatchesInString:<#theString#> options:0 range:NSMakeRange(0, <#theString#>.length)] == 1)
{
    // Yay, digits!
}

Предлагаю NSRegularExpressionгде-нибудь хранить экземпляр.


1

Мне нужно текстовое поле, в котором разрешены только целые числа. Вот что у меня получилось (используя информацию отсюда и из других источников):

Создайте средство форматирования целых чисел (в UIApplicationDelegate, чтобы его можно было использовать повторно):

@property (nonatomic, retain) NSNumberFormatter *integerNumberFormatter;

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Create and configure an NSNumberFormatter for integers
    integerNumberFormatter = [[NSNumberFormatter alloc] init];
    [integerNumberFormatter setMaximumFractionDigits:0];

    return YES;
}

Используйте фильтр в UITextFieldDelegate:

@interface MyTableViewController : UITableViewController <UITextFieldDelegate> {
    ictAppDelegate *appDelegate;
}

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    // Make sure the proposed string is a number
    NSNumberFormatter *inf = [appDelegate integerNumberFormatter];
    NSString* proposedString = [textField.text stringByReplacingCharactersInRange:range withString:string];
    NSNumber *proposedNumber = [inf numberFromString:proposedString];
    if (proposedNumber) {
        // Make sure the proposed number is an integer
        NSString *integerString = [inf stringFromNumber:proposedNumber];
        if ([integerString isEqualToString:proposedString]) {
            // proposed string is an integer
            return YES;
        }
    }

    // Warn the user we're rejecting the change
    AudioServicesPlayAlertSound(kSystemSoundID_Vibrate);
    return NO;
}

1

Не так изящно, но просто :)

- (BOOL) isNumber: (NSString*)input
{
    return [input doubleValue] != 0 || [input isEqualToString:@"0"] || [input isEqualToString:@"0.0"];
}

1
Это решение не работает с бесконечными нулями, такими как 0.00. Так что я не буду предлагать людям его использовать
Винь

1

Принимать десятичные значения в текстовых полях с одинарной точкой (.) При работе с iPad и iPhone в Swift 3

 func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        let inverseSet = NSCharacterSet(charactersIn:"0123456789").inverted

        let components = string.components(separatedBy: inverseSet)

        let filtered = components.joined(separator: "")

        if filtered == string {
            return true
        } else {
            if string == "." {
                let countdots = textField.text!.components(separatedBy:".").count - 1
                if countdots == 0 {
                    return true
                }else{
                    if countdots > 0 && string == "." {
                        return false
                    } else {
                        return true
                    }
                }
            }else{
                return false
            }
        }
    }

0

Чтобы быть более интернациональным (и не только американским ;-)), просто замените в приведенном выше коде на

-(NSNumber *) getNumber
{
  NSString* localeIdentifier = [[NSLocale autoupdatingCurrentLocale] localeIdentifier];
  NSLocale *l_en = [[NSLocale alloc] initWithLocaleIdentifier: localeIdentifier] ;
  return [self getNumberWithLocale: [l_en autorelease] ];
}

0

В этом ответе используется NSFormatter, как было сказано ранее. Проверьте это:

@interface NSString (NSNumber)
- (BOOL) isNumberWithLocale:(NSLocale *) stringLocale;  
- (BOOL) isNumber;
- (NSNumber *) getNumber; 
- (NSNumber *) getNumberWithLocale:(NSLocale*) stringLocale;
@end

@implementation NSString (NSNumber)
- (BOOL) isNumberWithLocale:(NSLocale *) stringLocale
{
    return [self getNumberWithLocale:stringLocale] != nil;
}
- (BOOL) isNumber
{
    return [ self getNumber ] != nil;
}
- (NSNumber *) getNumber
{
    NSLocale *l_en = [[NSLocale alloc] initWithLocaleIdentifier: @"en_US"] ;  
    return [self getNumberWithLocale: [l_en autorelease] ];
}

- (NSNumber *) getNumberWithLocale:(NSLocale*) stringLocale
{
    NSNumberFormatter *formatter = [[ [ NSNumberFormatter alloc ] init ] autorelease];
    [formatter setLocale: stringLocale ];
    return [ formatter numberFromString:self ]; 
}
@end

Надеюсь, это кому-то поможет. знак равно


0
   #import "NSString+Extension.h"

//@interface NSString (Extension)
//
//- (BOOL) isAnEmail;
//- (BOOL) isNumeric;
//
//@end

@implementation NSString (Extension)
 - (BOOL) isNumeric
    {
        NSString *emailRegex = @"[0-9]+";
        NSPredicate *emailTest = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", emailRegex];

        return [emailTest evaluateWithObject:self];

    //    NSString *localDecimalSymbol = [[NSLocale currentLocale] objectForKey:NSLocaleDecimalSeparator];
    //    NSMutableCharacterSet *decimalCharacterSet = [NSMutableCharacterSet characterSetWithCharactersInString:localDecimalSymbol];
    //    [decimalCharacterSet formUnionWithCharacterSet:[NSCharacterSet alphanumericCharacterSet]];
    //    
    //    NSCharacterSet* nonNumbers = [decimalCharacterSet invertedSet];
    //    NSRange r = [self rangeOfCharacterFromSet: nonNumbers];
    //    
    //    if (r.location == NSNotFound)
    //    {
    //        // check to see how many times the decimal symbol appears in the string. It should only appear once for the number to be numeric.
    //        int numberOfOccurances = [[self componentsSeparatedByString:localDecimalSymbol] count]-1;
    //        return (numberOfOccurances > 1) ? NO : YES;
    //    }
    //    else return NO;
    }

0

В Swift 4:

let formatString = "12345"
if let number = Decimal(string:formatString){

    print("String contains only number")
}
else{
    print("String doesn't contains only number")
}

0

Это включает: управление десятичной частью (включая количество разрешенных десятичных знаков), управление копированием / вставкой, международные разделители.

Шаги:

  1. Убедитесь, что ваш контроллер представления наследуется от UITextFieldDelegate

    класс MyViewController: UIViewController, UITextFieldDelegate {...

  2. В viewDidLoad установите для своего управляющего делегата self:

    переопределить функцию viewDidLoad () {super.viewDidLoad (); yourTextField.delegate = self}

  3. Реализуйте следующий метод и обновите константу decsAllowed с желаемым количеством десятичных знаков или 0, если вы хотите натуральное число.

Swift 4

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

    let decsAllowed: Int = 2
    let candidateText = NSString(string: textField.text!).replacingCharacters(in: range, with: string)
    let decSeparator: String = NumberFormatter().decimalSeparator!;

    let splitted = candidateText.components(separatedBy: decSeparator)
    let decSeparatorsFound = splitted.count - 1
    let decimalPart = decSeparatorsFound > 0 ? splitted.last! : ""
    let decimalPartCount = decimalPart.characters.count

    let characterSet = NSMutableCharacterSet.decimalDigit()
    if decsAllowed > 0 {characterSet.addCharacters(in: decSeparator)}

    let valid = characterSet.isSuperset(of: CharacterSet(charactersIn: candidateText)) &&
                decSeparatorsFound <= 1 &&
                decsAllowed >= decimalPartCount

    return valid
}

Если после этого вам нужно безопасно преобразовать эту строку в число, вы можете просто использовать приведение типа Double (yourstring) или Int (yourstring) или более академическим способом:

let formatter = NumberFormatter()
let theNumber: NSNumber = formatter.number(from: yourTextField.text)!
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.