From 57c60deda2467a9adabce769457951d28e903e31 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Thu, 16 Feb 2017 21:26:26 -0500 Subject: [PATCH] Further refine the registration and verification views. // FREEBIE --- .../CodeVerificationViewController.m | 37 ++++------ .../RegistrationViewController.m | 73 +++++++++++-------- 2 files changed, 58 insertions(+), 52 deletions(-) diff --git a/Signal/src/view controllers/CodeVerificationViewController.m b/Signal/src/view controllers/CodeVerificationViewController.m index d72e65551..d7aeb2bac 100644 --- a/Signal/src/view controllers/CodeVerificationViewController.m +++ b/Signal/src/view controllers/CodeVerificationViewController.m @@ -139,6 +139,7 @@ NSString *const kCompletedRegistrationSegue = @"CompletedRegistration"; @"Text field placeholder for SMS verification code during registration"); _challengeTextField.font = [UIFont ows_lightFontWithSize:21.f]; _challengeTextField.textAlignment = NSTextAlignmentCenter; + _challengeTextField.keyboardType = UIKeyboardTypePhonePad; _challengeTextField.delegate = self; [self.view addSubview:_challengeTextField]; [_challengeTextField autoPinWidthToSuperviewWithMargin:kHMargin]; @@ -392,18 +393,6 @@ NSString *const kCompletedRegistrationSegue = @"CompletedRegistration"; [self.view endEditing:NO]; } -- (NSString *)stringByFilteringString:(NSString *)input - withCharacterSet:(NSCharacterSet *)characterSet { - NSMutableString *result = [NSMutableString new]; - for (NSUInteger i=0; i < input.length; i++) { - unichar c = [input characterAtIndex:i]; - if ([characterSet characterIsMember:c]) { - [result appendFormat:@"%c", c]; - } - } - return [result copy]; -} - - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)insertionText { @@ -423,18 +412,14 @@ NSString *const kCompletedRegistrationSegue = @"CompletedRegistration"; // * Take partial input if possible. NSString *oldText = textField.text; - NSCharacterSet *validCharacterSet = [NSCharacterSet decimalDigitCharacterSet]; // Construct the new contents of the text field by: // 1. Determining the "left" substring: the contents of the old text _before_ the deletion range. // Filtering will remove non-decimal digit characters like hyphen "-". - NSString *left = [self stringByFilteringString:[oldText substringToIndex:range.location] - withCharacterSet:validCharacterSet]; + NSString *left = [oldText substringToIndex:range.location].digitsOnly; // 2. Determining the "right" substring: the contents of the old text _after_ the deletion range. - NSString *right = [self stringByFilteringString:[oldText substringFromIndex:range.location + range.length] - withCharacterSet:validCharacterSet]; + NSString *right = [oldText substringFromIndex:range.location + range.length].digitsOnly; // 3. Determining the "center" substring: the contents of the new insertion text. - NSString *center = [self stringByFilteringString:insertionText - withCharacterSet:validCharacterSet]; + NSString *center = insertionText.digitsOnly; // 3a. Trim the tail of the "center" substring to ensure that we don't end up // with more than 6 decimal digits. while (center.length > 0 && @@ -474,9 +459,17 @@ NSString *const kCompletedRegistrationSegue = @"CompletedRegistration"; } - (void)setVerificationCodeAndTryToVerify:(NSString *)verificationCode { - NSCharacterSet *validCharacterSet = [NSCharacterSet decimalDigitCharacterSet]; - self.challengeTextField.text = [self stringByFilteringString:verificationCode - withCharacterSet:validCharacterSet]; + NSString *rawNewText = verificationCode.digitsOnly; + NSString *formattedNewText = (rawNewText.length <= 3 + ? rawNewText + : [[[rawNewText substringToIndex:3] + stringByAppendingString:@"-"] + stringByAppendingString:[rawNewText substringFromIndex:3]]); + self.challengeTextField.text = formattedNewText; + // Move the cursor after the newly inserted text. + UITextPosition *newPosition = [self.challengeTextField endOfDocument]; + self.challengeTextField.selectedTextRange = [self.challengeTextField textRangeFromPosition:newPosition + toPosition:newPosition]; [self verifyChallengeAction:nil]; } diff --git a/Signal/src/view controllers/RegistrationViewController.m b/Signal/src/view controllers/RegistrationViewController.m index c36b87659..5ae7a1169 100644 --- a/Signal/src/view controllers/RegistrationViewController.m +++ b/Signal/src/view controllers/RegistrationViewController.m @@ -26,6 +26,7 @@ static NSString *const kCodeSentSegue = @"codeSent"; // Do any additional setup after loading the view. _phoneNumberTextField.delegate = self; + _phoneNumberTextField.keyboardType = UIKeyboardTypePhonePad; [self populateDefaultCountryNameAndCode]; [[Environment getCurrent] setSignUpFlowNavigationController:self.navigationController]; @@ -148,48 +149,60 @@ static NSString *const kCodeSentSegue = @"codeSent"; - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range - replacementString:(NSString *)string { - NSString *textBeforeChange = textField.text; - - // backspacing should skip over formatting characters - UITextPosition *posIfBackspace = [textField positionFromPosition:textField.beginningOfDocument - offset:(NSInteger)(range.location + range.length)]; - UITextRange *rangeIfBackspace = [textField textRangeFromPosition:posIfBackspace toPosition:posIfBackspace]; - bool isBackspace = - string.length == 0 && range.length == 1 && [rangeIfBackspace isEqual:textField.selectedTextRange]; - if (isBackspace) { - NSString *digits = textBeforeChange.digitsOnly; - NSUInteger correspondingDeletePosition = [PhoneNumberUtil translateCursorPosition:range.location + range.length - from:textBeforeChange - to:digits - stickingRightward:true]; - if (correspondingDeletePosition > 0) { - textBeforeChange = digits; - range = NSMakeRange(correspondingDeletePosition - 1, 1); - } - } - - // make the proposed change - NSString *textAfterChange = [textBeforeChange withCharactersInRange:range replacedBy:string]; - NSUInteger cursorPositionAfterChange = range.location + string.length; - + replacementString:(NSString *)insertionText { + + // Phone numbers takes many forms. + // + // * We only want to let the user enter decimal digits. + // * The user shouldn't have to enter hyphen, parentheses or whitespace; + // the phone number should be formatted automatically. + // * The user should be able to copy and paste freely. + // * Invalid input should be simply ignored. + // + // We accomplish this by being permissive and trying to "take as much of the user + // input as possible". + // + // * Always accept deletes. + // * Ignore invalid input. + // * Take partial input if possible. + + NSString *oldText = textField.text; + // Construct the new contents of the text field by: + // 1. Determining the "left" substring: the contents of the old text _before_ the deletion range. + // Filtering will remove non-decimal digit characters like hyphen "-". + NSString *left = [oldText substringToIndex:range.location].digitsOnly; + // 2. Determining the "right" substring: the contents of the old text _after_ the deletion range. + NSString *right = [oldText substringFromIndex:range.location + range.length].digitsOnly; + // 3. Determining the "center" substring: the contents of the new insertion text. + NSString *center = insertionText.digitsOnly; + // 4. Construct the "raw" new text by concatenating left, center and right. + NSString *textAfterChange = [[left stringByAppendingString:center] + stringByAppendingString:right]; + // 5. Construct the "formatted" new text by inserting a hyphen if necessary. // reformat the phone number, trying to keep the cursor beside the inserted or deleted digit - bool isJustDeletion = string.length == 0; + bool isJustDeletion = insertionText.length == 0; + NSUInteger cursorPositionAfterChange = left.length + center.length; NSString *textAfterReformat = - [PhoneNumber bestEffortFormatPartialUserSpecifiedTextToLookLikeAPhoneNumber:textAfterChange.digitsOnly - withSpecifiedCountryCodeString:_countryCodeButton.titleLabel.text]; + [PhoneNumber bestEffortFormatPartialUserSpecifiedTextToLookLikeAPhoneNumber:textAfterChange + withSpecifiedCountryCodeString:_countryCodeButton.titleLabel.text]; NSUInteger cursorPositionAfterReformat = [PhoneNumberUtil translateCursorPosition:cursorPositionAfterChange from:textAfterChange to:textAfterReformat stickingRightward:isJustDeletion]; textField.text = textAfterReformat; UITextPosition *pos = - [textField positionFromPosition:textField.beginningOfDocument offset:(NSInteger)cursorPositionAfterReformat]; + [textField positionFromPosition:textField.beginningOfDocument offset:(NSInteger)cursorPositionAfterReformat]; [textField setSelectedTextRange:[textField textRangeFromPosition:pos toPosition:pos]]; - + return NO; // inform our caller that we took care of performing the change } +- (BOOL)textFieldShouldReturn:(UITextField *)textField { + [self sendCodeAction:nil]; + [textField resignFirstResponder]; + return NO; +} + #pragma mark - Unwind segue - (IBAction)unwindToChangeNumber:(UIStoryboardSegue *)sender {