Форматирование поля UITextField для ввода кредитной карты, например (xxxx xxxx xxxx xxxx)
Я хочу отформатировать a UITextField
для ввода номера кредитной карты таким образом, что он позволяет вводить только цифры и автоматически вставляет пробелы, чтобы номер был отформатирован следующим образом:
XXXX XXXX XXXX XXXX
Как я могу это сделать?
1 ответ:
Если вы используете Swift, перейдите читать мой порт этого ответа для Swift 4 и использовать это вместо этого.
Если вы находитесь в объектив-С...
во-первых, ваш
UITextFieldDelegate
добавить эти переменные...NSString *previousTextFieldContent; UITextRange *previousSelection;
... и эти методы:
// Version 1.3 // Source and explanation: http://stackoverflow.com/a/19161529/1709587 -(void)reformatAsCardNumber:(UITextField *)textField { // In order to make the cursor end up positioned correctly, we need to // explicitly reposition it after we inject spaces into the text. // targetCursorPosition keeps track of where the cursor needs to end up as // we modify the string, and at the end we set the cursor position to it. NSUInteger targetCursorPosition = [textField offsetFromPosition:textField.beginningOfDocument toPosition:textField.selectedTextRange.start]; NSString *cardNumberWithoutSpaces = [self removeNonDigits:textField.text andPreserveCursorPosition:&targetCursorPosition]; if ([cardNumberWithoutSpaces length] > 19) { // If the user is trying to enter more than 19 digits, we prevent // their change, leaving the text field in its previous state. // While 16 digits is usual, credit card numbers have a hard // maximum of 19 digits defined by ISO standard 7812-1 in section // 3.8 and elsewhere. Applying this hard maximum here rather than // a maximum of 16 ensures that users with unusual card numbers // will still be able to enter their card number even if the // resultant formatting is odd. [textField setText:previousTextFieldContent]; textField.selectedTextRange = previousSelection; return; } NSString *cardNumberWithSpaces = [self insertCreditCardSpaces:cardNumberWithoutSpaces andPreserveCursorPosition:&targetCursorPosition]; textField.text = cardNumberWithSpaces; UITextPosition *targetPosition = [textField positionFromPosition:[textField beginningOfDocument] offset:targetCursorPosition]; [textField setSelectedTextRange: [textField textRangeFromPosition:targetPosition toPosition:targetPosition] ]; } -(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { // Note textField's current state before performing the change, in case // reformatTextField wants to revert it previousTextFieldContent = textField.text; previousSelection = textField.selectedTextRange; return YES; } /* Removes non-digits from the string, decrementing `cursorPosition` as appropriate so that, for instance, if we pass in `@"1111 1123 1111"` and a cursor position of `8`, the cursor position will be changed to `7` (keeping it between the '2' and the '3' after the spaces are removed). */ - (NSString *)removeNonDigits:(NSString *)string andPreserveCursorPosition:(NSUInteger *)cursorPosition { NSUInteger originalCursorPosition = *cursorPosition; NSMutableString *digitsOnlyString = [NSMutableString new]; for (NSUInteger i=0; i<[string length]; i++) { unichar characterToAdd = [string characterAtIndex:i]; if (isdigit(characterToAdd)) { NSString *stringToAdd = [NSString stringWithCharacters:&characterToAdd length:1]; [digitsOnlyString appendString:stringToAdd]; } else { if (i < originalCursorPosition) { (*cursorPosition)--; } } } return digitsOnlyString; } /* Detects the card number format from the prefix, then inserts spaces into the string to format it as a credit card number, incrementing `cursorPosition` as appropriate so that, for instance, if we pass in `@"111111231111"` and a cursor position of `7`, the cursor position will be changed to `8` (keeping it between the '2' and the '3' after the spaces are added). */ - (NSString *)insertCreditCardSpaces:(NSString *)string andPreserveCursorPosition:(NSUInteger *)cursorPosition { // Mapping of card prefix to pattern is taken from // https://baymard.com/checkout-usability/credit-card-patterns // UATP cards have 4-5-6 (XXXX-XXXXX-XXXXXX) format bool is456 = [string hasPrefix: @"1"]; // These prefixes reliably indicate either a 4-6-5 or 4-6-4 card. We treat all // these as 4-6-5-4 to err on the side of always letting the user type more // digits. bool is465 = [string hasPrefix: @"34"] || [string hasPrefix: @"37"] || // Diners Club [string hasPrefix: @"300"] || [string hasPrefix: @"301"] || [string hasPrefix: @"302"] || [string hasPrefix: @"303"] || [string hasPrefix: @"304"] || [string hasPrefix: @"305"] || [string hasPrefix: @"309"] || [string hasPrefix: @"36"] || [string hasPrefix: @"38"] || [string hasPrefix: @"39"]; // In all other cases, assume 4-4-4-4-3. // This won't always be correct; for instance, Maestro has 4-4-5 cards // according to https://baymard.com/checkout-usability/credit-card-patterns, // but I don't know what prefixes identify particular formats. bool is4444 = !(is456 || is465); NSMutableString *stringWithAddedSpaces = [NSMutableString new]; NSUInteger cursorPositionInSpacelessString = *cursorPosition; for (NSUInteger i=0; i<[string length]; i++) { bool needs465Spacing = (is465 && (i == 4 || i == 10 || i == 15)); bool needs456Spacing = (is456 && (i == 4 || i == 9 || i == 15)); bool needs4444Spacing = (is4444 && i > 0 && (i % 4) == 0);