Last active
January 7, 2020 15:35
-
-
Save jcoleman/6831014 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
- (void) setExpirationTextField:(UITextField*)expirationTextField { | |
_expirationTextField = expirationTextField; | |
JCTextFieldSetKeyboardToDigits(expirationTextField); | |
} | |
- (void) setCardNumberTextField:(UITextField*)cardNumberTextField { | |
_cardNumberTextField = cardNumberTextField; | |
JCTextFieldSetKeyboardToDigits(cardNumberTextField); | |
} | |
- (BOOL) textField:(UITextField*)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString*)replacementString { | |
if (textField == self.expirationTextField) {\ | |
NSArray* digitSets = JCTextFieldHandleCharactersChangeForDigitSetsField(textField, range, replacementString, @[@(2), @(4)], @" / "); | |
if (!inTextFieldUpdateRecursion) { | |
inTextFieldUpdateRecursion = YES; | |
self.creditCard.expirationMonth = digitSets[0]; | |
self.creditCard.expirationYear = digitSets[1]; | |
inTextFieldUpdateRecursion = NO; | |
} | |
return NO; | |
} else if (textField == self.cardNumberTextField) { | |
__block NSArray* digitSetsLengths = nil; | |
[self.cardNumberSectionDigitCountsByCardNumberPrefix enumerateKeysAndObjectsUsingBlock:^(NSString* prefix, NSArray* _digitSetLengths, BOOL* stop) { | |
if ( (range.location == 0 && [[textField.text stringByReplacingCharactersInRange:range withString:replacementString] hasPrefix:prefix]) | |
|| (range.location != 0 && [textField.text hasPrefix:prefix])) { | |
digitSetsLengths = _digitSetLengths; | |
*stop = YES; | |
} | |
}]; | |
if (!digitSetsLengths) { | |
digitSetsLengths = @[@(4), @(4), @(4), @(4)]; | |
} | |
NSArray* digitSets = JCTextFieldHandleCharactersChangeForDigitSetsField(textField, range, replacementString, digitSetsLengths, @" "); | |
if (!inTextFieldUpdateRecursion) { | |
inTextFieldUpdateRecursion = YES; | |
self.creditCard.cardNumber = [[digitSets componentsJoinedByString:@""] stringByReplacingOccurrencesOfString:@"_" withString:@""]; | |
inTextFieldUpdateRecursion = NO; | |
} | |
return NO; | |
} else { | |
return YES; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <Foundation/Foundation.h> | |
#ifndef GlobalDriver2Passenger_JCTextFieldFunctions_h | |
#define GlobalDriver2Passenger_JCTextFieldFunctions_h | |
NSArray* JCTextFieldHandleCharactersChangeForDigitSetsField(UITextField* textField, NSRange range, NSString* replacementString, NSArray* digitSetLengths, NSString* separator); | |
void JCTextFieldSetKeyboardToDigits(UITextField* textField); | |
#endif |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import "JCTextFieldFunctions.h" | |
@implementation NSString (OFStringHelpers) | |
- (NSString*) stringByRemovingNonDigitsAndUnderscores { | |
NSMutableString* string = [[NSMutableString alloc] initWithCapacity:self.length]; | |
size_t bufferSize = 32; | |
NSRange range = NSMakeRange(0, bufferSize); | |
NSUInteger end = [self length]; | |
while (range.location < end) { | |
unichar buffer[bufferSize]; | |
unichar substringBuffer[bufferSize]; | |
NSUInteger substringLength = 0; | |
if (range.location + range.length > end) { | |
range.length = end - range.location; | |
} | |
[self getCharacters:buffer range:range]; | |
range.location += bufferSize; | |
for (NSUInteger i = 0 ; i < range.length; ++i) { | |
unichar c = buffer[i]; | |
if ((c >= '0' && c <= '9') || c == '_') { | |
substringBuffer[substringLength] = c; | |
++substringLength; | |
} | |
} | |
[string appendString:[NSString stringWithCharacters:substringBuffer length:substringLength]]; | |
} | |
return string; | |
} | |
@end | |
NSArray* JCTextFieldHandleCharactersChangeForDigitSetsField(UITextField* textField, NSRange range, NSString* replacementString, NSArray* digitSetLengths, NSString* separator) { | |
NSString* existingText = (textField.text ? textField.text : @""); | |
NSString* textWithReplacements = [existingText stringByReplacingCharactersInRange:range withString:replacementString]; | |
NSString* filteredExistingText = [existingText stringByRemovingNonDigitsAndUnderscores]; | |
NSString* filterdTextWithReplacements = [textWithReplacements stringByRemovingNonDigitsAndUnderscores]; | |
NSString* filteredReplacementString = [replacementString stringByRemovingNonDigitsAndUnderscores]; | |
NSRange filteredRange = NSMakeRange([[existingText substringToIndex:range.location] stringByRemovingNonDigitsAndUnderscores].length, | |
[[existingText substringWithRange:range] stringByRemovingNonDigitsAndUnderscores].length); | |
if (filteredRange.length < filteredReplacementString.length) { | |
// Net addition of characters; overwrite any available underscores. | |
NSUInteger filteredRangeEnd = filteredRange.location + filteredRange.length; | |
NSUInteger availableUnderscores = 0; | |
if (filteredExistingText.length > filteredRangeEnd) { | |
availableUnderscores = [filteredExistingText rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@"_"] | |
options:NSAnchoredSearch | |
range:NSMakeRange(filteredRangeEnd, filteredExistingText.length - filteredRangeEnd)].length; | |
} | |
if (availableUnderscores) { | |
NSUInteger replacedUnderscores = MIN(availableUnderscores, filteredReplacementString.length - filteredRange.length); | |
filterdTextWithReplacements = [filteredExistingText stringByReplacingCharactersInRange:NSMakeRange(filteredRange.location, filteredRange.length + replacedUnderscores) | |
withString:filteredReplacementString]; | |
} | |
} else if (filteredRange.length > filteredReplacementString.length) { | |
// Net subtraction of characters; replace removed characters with underscores. | |
NSUInteger neededUnderscores = filteredRange.length - filteredReplacementString.length; | |
NSMutableString* underscores = [[NSMutableString alloc] initWithCapacity:neededUnderscores]; | |
for (NSUInteger i = 0; i < neededUnderscores; ++i) { | |
[underscores appendFormat:@"_"]; | |
} | |
filterdTextWithReplacements = [filteredExistingText stringByReplacingCharactersInRange:NSMakeRange(filteredRange.location, filteredRange.length) | |
withString:[filteredReplacementString stringByAppendingString:underscores]]; | |
} | |
NSUInteger expectedDigitCount = 0; | |
for (NSNumber* digitSetLength in digitSetLengths) { | |
expectedDigitCount += [digitSetLength unsignedIntegerValue]; | |
} | |
NSUInteger expectedStringLength = expectedDigitCount + ((digitSetLengths.count - 1) * separator.length); | |
NSMutableString* paddedFilterdTextWithReplacements = [NSMutableString stringWithString:filterdTextWithReplacements]; | |
if (expectedDigitCount > filterdTextWithReplacements.length) { | |
for (NSUInteger i = 0, len = expectedDigitCount - filterdTextWithReplacements.length; i < len; ++i) { | |
[paddedFilterdTextWithReplacements appendString:@"_"]; | |
} | |
} | |
NSUInteger index = range.location + replacementString.length; | |
NSString* textWithReplacementsToIndex = [textWithReplacements substringToIndex:index]; | |
NSUInteger filteredIndex = index - (textWithReplacementsToIndex.length - [textWithReplacementsToIndex stringByRemovingNonDigitsAndUnderscores].length); | |
NSMutableArray* digitSets = [[NSMutableArray alloc] init]; | |
NSUInteger separatorsBeforeIndex = 0; | |
BOOL filteredIndexIsAtSeparatorBoundary = NO; | |
NSUInteger currentDigitSetsDigitStringIndex = 0; | |
for (NSNumber* _digitSetLength in digitSetLengths) { | |
NSUInteger digitSetLength = [_digitSetLength unsignedIntegerValue]; | |
[digitSets addObject:[paddedFilterdTextWithReplacements substringWithRange:NSMakeRange(currentDigitSetsDigitStringIndex, digitSetLength)]]; | |
currentDigitSetsDigitStringIndex += digitSetLength; | |
if (filteredIndex > currentDigitSetsDigitStringIndex) { | |
++separatorsBeforeIndex; | |
} else if (filteredIndex == currentDigitSetsDigitStringIndex) { | |
filteredIndexIsAtSeparatorBoundary = YES; | |
} | |
} | |
if (filteredIndexIsAtSeparatorBoundary && filteredReplacementString.length > filteredRange.length) { | |
++separatorsBeforeIndex; | |
} | |
NSString* text = [digitSets componentsJoinedByString:separator]; | |
textField.text = text; | |
NSUInteger calcuatedCursorIndex = (separatorsBeforeIndex * separator.length) + filteredIndex; | |
NSUInteger cursorIndex = MIN(calcuatedCursorIndex, expectedStringLength); | |
UITextPosition* start = [textField positionFromPosition:[textField beginningOfDocument] | |
offset:cursorIndex]; | |
UITextPosition* end = [textField positionFromPosition:start | |
offset:0]; | |
[textField setSelectedTextRange:[textField textRangeFromPosition:start toPosition:end]]; | |
return digitSets; | |
} | |
void JCTextFieldSetKeyboardToDigits(UITextField* textField) { | |
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) { | |
textField.keyboardType = UIKeyboardTypeNumbersAndPunctuation; | |
} else { | |
textField.keyboardType = UIKeyboardTypeDecimalPad; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment