Jak przetestować wprowadzanie znaków do UITextField, gdy użytkownik wprowadza znaki i zapobiega nieprawidłowym znakom

Najpierw skonfigurowałem klawiaturę, aby pole UITextField używało liczby w stylu dziesiętnym. Tak więc użytkownik może wprowadzać tylko liczby i jeden dziesiętny.

To, co chcę zrobić, to przetestować dane wejściowe, gdy użytkownik je wprowadzi i zapobiec wprowadzaniu wielu miejsc po przecinku i ograniczyć część dziesiętną liczby do dwóch miejsc. Nie chcę zaokrąglać liczby ani nawet traktować wejścia jako liczby. Po prostu chcę uniemożliwić użytkownikowi wprowadzenie więcej niż dwóch cyfr po prawej stronie miejsce po przecinku.

Author: Seamus, 2012-02-18

6 answers

Ostatecznie rozwiązanie okazało się dość trywialne. Niestety wiele pytań i odpowiedzi związanych z tym pytaniem dotyczy walidacji lub formatowania wartości liczbowych, a nie kontrolowania tego, co użytkownik może wprowadzić.

Poniższą implementacją metody shouldChangeCharactersInRange delegate jest moje rozwiązanie. Jak zawsze, wyrażenia regularne sprawdzają się w tej sytuacji. RegExLib.com jest doskonałym źródłem przydatnych próbek RegEx lub rozwiązań. Nie jestem RegEx guru i zawsze trochę walczą, składając je razem, więc wszelkie sugestie dotyczące poprawy są mile widziane.

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

        NSString *expression = @"^([0-9]+)?(\\.([0-9]{1,2})?)?$";

        NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:expression 
                                                                               options:NSRegularExpressionCaseInsensitive 
                                                                                 error:nil];
        NSUInteger numberOfMatches = [regex numberOfMatchesInString:newString
                                                            options:0
                                                              range:NSMakeRange(0, [newString length])];        
        if (numberOfMatches == 0)
            return NO;        
    }

    return YES;
}

Powyższy kod pozwala użytkownikowi wprowadzić takie wartości: 1, 1.1, 1.11,.1, .11. Zauważ, że ta implementacja nie zastępuje ciągu pola tekstowego, co powoduje rekurencję. Rozwiązanie to po prostu odrzuca następny znak wprowadzany przez użytkownika, jeśli' newString ' nie pasuje do wyrażenia regularnego.

 45
Author: Seamus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-14 11:57:48

Dobrze! Jeśli ktoś potrzebuje pracować z walutami spoza USA: euro lub innymi z ułamkiem oddzielonym przecinkiem zamiast kropką użyj tego:

NSString *expression = @"^([0-9]+)?([\\,\\.]([0-9]{1,2})?)?$";

Jedyną różnicą jest to, że pozwala na przecinek lub kropkę. ([\,\.- ani jedno, ani drugie . albo,) jedyny trik polega na tym, że trzeba zastąpić przecinek kropką przy użyciu nabytej liczby, ponieważ komputer używa kropki do oddzielania ułamka, a nie przecinka.

 5
Author: Vladimir Shutyuk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-07 13:46:05

Standardowym sposobem radzenia sobie z tym problemem w systemie iOS jest dołączenie UITextFieldDelegate do swojej metody UITextField i zaimplementowanie metody textField:shouldChangeCharactersInRange:replacementString:. Wewnątrz tej metody można zweryfikować ciąg znaków, aby był prawidłowym "kształtem" dla swoich celów (jedna kropka, nie więcej niż dwie cyfry po kropce, itd.) i podać inny ciąg znaków, jeśli Dane wejściowe nie są zgodne z oczekiwanym formatem.

 3
Author: dasblinkenlight,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-18 19:06:00

Po wielu eksperymentach i przyjrzeniu się innym rozwiązaniom (które wydają się zbyt skomplikowane i skrzypiące) wymyśliłem następujące, co uniemożliwia użytkownikowi wprowadzanie czegokolwiek poza ważną kwotą waluty. Zasadniczo, niech instancja NSNumberFormatter (utworzona gdzie indziej w viewController) wykona ciężką pracę:

  • 1) przekonwertować tekst na liczbę (jeśli nil, to są nieprawidłowe znaki, więc zwracają nie)
  • 2) następnie Przelicz liczbę na walutę
  • 3) następnie Przelicz ciąg waluty z powrotem na numer i, jeśli jest inny niż oryginalny (oznacza zbyt wiele wpisanych miejsc po przecinku), zwróć NO
  • 4) następnie zrobić NSDecimalNumber, który jest to, co chciałem. Może być inaczej dla ciebie, oczywiście. Jak na razie działa u mnie-mam nadzieję, że to komuś pomoże.

Najpierw ustawiłem klawiaturę na UIKeyboardTypeDecimalPad. Następnie użyłem następującego kodu

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

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

[numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
NSNumber *amountNumber = [numberFormatter numberFromString:textString];

if ([textString length] > 0) {
    if (!amountNumber) {
        return NO;
    }
} else {
    amountNumber = @0;
}


[numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
NSString *amountStringer = [numberFormatter stringFromNumber:amountNumber];
NSNumber *amountAgain = [numberFormatter numberFromString:amountStringer];

//
//make sure that the number obtained again is the same....prevents too many decimals....
if (![amountNumber isEqualToNumber:amountAgain]) {
    return NO;
}

[numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
amountShow.text = amountStringer;

self.amount = [NSDecimalNumber decimalNumberWithDecimal:[amountAgain decimalValue]];
NSLog(@"decimal Amount is %@", self.amount);

return YES;

}

 1
Author: user3228583,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-20 18:36:14

Dla Swift 4 główna odpowiedź na pytanie to

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    guard textField == quantityTextField, let textFieldString = textField.text as NSString? else {
        return true
    }

    let newString = textFieldString.replacingCharacters(in: range, with: string)
    let expression = "^([0-9]+)?(\\.([0-9]{1,2})?)?$"

    let regex = try? NSRegularExpression(pattern: expression, options: .caseInsensitive)
    let numberOfMathces = regex?.numberOfMatches(in: newString, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: NSMakeRange(0, newString.characters.count))

    if numberOfMathces == 0 {
        return false
    }

    return true
}
 0
Author: Malder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-27 17:09:45

Możesz przekazać łańcuch uitexfield do poniższej metody, zwróci ona yes / no..w związku z tym możesz pokazać błąd

- (BOOL) validatePhone: (NSString *) aMobile {
    NSString *phoneRegex = @"^+(?:[0-9] ?){6,14}[0-9]$"; 
    NSPredicate *phoneTest = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", phoneRegex];
    if (aMobile.length>0) {
        NSString *mobileTextString = [[mobileText.text componentsSeparatedByCharactersInSet:
                                       [[NSCharacterSet decimalDigitCharacterSet] invertedSet]] 
                                      componentsJoinedByString:@""];
        NSString *firstNumber = [aMobile substringToIndex:1];
       if (mobileTextString.length!=10){

            return NO;
        }

    }
        return [phoneTest evaluateWithObject:aMobile];
}
 -1
Author: Priyanka Singh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-26 13:38:06